#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <windows.h>
#include <dbt.h>

#include <iostream>
#include <variant>
#include <string>

#include <QFile>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QSpacerItem>
#include <QMessageBox>
#include <QDebug>

#include "util.h"


using namespace std;


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    InitUI();
    InitSerilaPort();

    slot_ShowSerialStateInfo("");

    // 初始化串口ring buffer
    lwrb_init(&m_serialLwrb, m_serialLwrbBuff, m_serialLwrbBuffSize);

    // “调试”界面两个示波器的数据队列
    m_queue_debugPageScope1 = new QQueue<float>;
    m_queue_debugPageScope2 = new QQueue<float>;
    // “校准”界面两个示波器的校准数据容器
    m_vector_calibPageScope1 = new QVector<float>;
    m_vector_calibPageScope2 = new QVector<float>;

    // 创建串口命令下发定时器，通信频率1K
    m_tim_serialCmdSend = new QTimer();
    connect(m_tim_serialCmdSend, &QTimer::timeout, this, &MainWindow::slot_TimSerialCmdSend_Timeout);
    m_tim_serialCmdSend->start(1);

    // 串口队列数据处理线程
    m_serialDataConsumer = new CSerialDataConsumer(&m_serialLwrb, m_map_indexToConfig,  m_map_configToDataType,
                                                   m_queue_debugPageScope1, m_queue_debugPageScope2,
                                                   m_vector_calibPageScope1, m_vector_calibPageScope2);
    // 线程对象发射信号，对应的槽函数执行相应操作
    connect(m_serialDataConsumer, &CSerialDataConsumer::signal_SetMotorEnableLed, this, &MainWindow::slot_SetMotorEnableLed);
    connect(m_serialDataConsumer, &CSerialDataConsumer::signal_SetTargetReachedLed, this, &MainWindow::slot_SetTargetReachedLed);
    connect(m_serialDataConsumer, &CSerialDataConsumer::signal_SetCurrentLimitActiveLed, this, &MainWindow::slot_SetCurrentLimitActiveLed);
    connect(m_serialDataConsumer, &CSerialDataConsumer::signal_ShowRxErrInfo, this, &MainWindow::slot_ShowRxErrInfo);
    connect(m_serialDataConsumer, &CSerialDataConsumer::signal_ShowTxErrInfo, this, &MainWindow::slot_ShowTxErrInfo);
    connect(m_serialDataConsumer, &CSerialDataConsumer::signal_ShowSystemStatsInfo, this, &MainWindow::slot_ShowSystemStateInfo);
    // 线程发送校准结果信号
    connect(m_serialDataConsumer, &CSerialDataConsumer::signal_SetCalibPageMotorR, this, &MainWindow::slot_SetCalibPageMotorR);
    connect(m_serialDataConsumer, &CSerialDataConsumer::signal_SetCalibPageMotorL, this, &MainWindow::slot_SetCalibPageMotorL);
    connect(m_serialDataConsumer, &CSerialDataConsumer::signal_SetCalibPageMotorPP, this, &MainWindow::slot_SetCalibPageMotorPP);
    connect(m_serialDataConsumer, &CSerialDataConsumer::signal_SetCalibPageEncoderDir, this, &MainWindow::slot_SetCalibPageEncoderDir);
    connect(m_serialDataConsumer, &CSerialDataConsumer::signal_SetCalibPageEncoderOffset, this, &MainWindow::slot_SetCalibPageEncoderOffset);
    // 线程发送读取配置的信号
    connect(m_serialDataConsumer, &CSerialDataConsumer::signal_ReadConfig, this, &MainWindow::slot_ReadConfig);

    // 线程对象
    QThread* thread = new QThread;
    m_serialDataConsumer->moveToThread(thread);
    thread->start();
    // 使用一个一次性定时器来触发死循环线程工作
    QTimer* tim = new QTimer;
    connect(tim, &QTimer::timeout, m_serialDataConsumer, &CSerialDataConsumer::working);
    tim->setSingleShot(true);
    tim->start(1);

    // 创建绘图定时器
    m_tim_qcpPlot = new QTimer();
    connect(m_tim_qcpPlot, &QTimer::timeout, this, &MainWindow::slot_TimQcpPlot_Timeout);
    m_tim_qcpPlot->start(1); // max fps
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::InitUI()
{
    // 设置窗口最小尺寸
    this->setMinimumSize(1026, 672);

    InitToolBar();
    InitStateBar();

    m_widget_debug = new QWidget();
    m_widget_calib = new QWidget();
    m_widget_config = new QWidget();
    m_widget_about = new QWidget();

    ui->stackedWidget->addWidget(m_widget_debug);
    ui->stackedWidget->addWidget(m_widget_calib);
    ui->stackedWidget->addWidget(m_widget_config);
    ui->stackedWidget->addWidget(m_widget_about);
    ui->stackedWidget->setCurrentWidget(m_widget_debug);

    InitDebugUI();
    InitCalibUI();
    InitConfigUI();

    // 加载qss样式表
    QFile qss(":/Config/app.qss");
    qss.open(QFile::ReadOnly);
    //this->setStyleSheet(qss.readAll());
    qss.close();
}

void MainWindow::InitToolBar()
{
    QWidget* spacer = nullptr;
    QLabel* label = nullptr;

    // 设置窗口图标、标题
    this->setWindowIcon(QIcon(":/Icon/motor.png"));
    this->setWindowTitle("easy_dgm_tool");

    // 设置toolbar属性
    ui->toolBar->setMovable(false);
    ui->toolBar->setFloatable(false);

    // 添加调试按钮
    m_btn_debug = new QToolButton(this);
    m_btn_debug->setToolButtonStyle(Qt::ToolButtonStyle::ToolButtonTextBesideIcon);
    m_btn_debug->setIcon(QIcon(":/Icon/debug.png"));
    m_btn_debug->setText("调试");
    ui->toolBar->addWidget(m_btn_debug);
    connect(m_btn_debug, &QToolButton::clicked, this, &MainWindow::slot_BtnDebug_Clicked);

    // 添加校准按钮
    m_btn_calib = new QToolButton(this);
    m_btn_calib->setToolButtonStyle(Qt::ToolButtonStyle::ToolButtonTextBesideIcon);
    m_btn_calib->setIcon(QIcon(":/Icon/calib.png"));
    m_btn_calib->setText("校准");
    ui->toolBar->addWidget(m_btn_calib);
    connect(m_btn_calib, &QToolButton::clicked, this, &MainWindow::slot_BtnCalib_Clicked);

    // 添加配置按钮
    m_btn_config = new QToolButton(this);
    m_btn_config->setToolButtonStyle(Qt::ToolButtonStyle::ToolButtonTextBesideIcon);
    m_btn_config->setIcon(QIcon(":/Icon/config.png"));
    m_btn_config->setText("配置");
    ui->toolBar->addWidget(m_btn_config);
    connect(m_btn_config, &QToolButton::clicked, this, &MainWindow::slot_BtnConfig_Clicked);

    // 创建位控模式下的曲线轨迹规划界面
    m_trackGenerate = new CTrackGenerate();
    connect(m_trackGenerate, &CTrackGenerate::signal_SendPos, this, &MainWindow::slot_BtnTrackGeneratePageSendPos); // 绑定接收外部位置信号的槽函数
    // 添加轨迹按钮
    m_btn_track = new QToolButton(this);
    m_btn_track->setToolButtonStyle(Qt::ToolButtonStyle::ToolButtonTextBesideIcon);
    m_btn_track->setIcon(QIcon(":/Icon/track.png"));
    m_btn_track->setText("位置轨迹");
    ui->toolBar->addWidget(m_btn_track);
    connect(m_btn_track, &QToolButton::clicked, m_trackGenerate, [=]{
        m_trackGenerate->SetPageShow(true);
    });

    // 添加关于按钮
    m_btn_about = new QToolButton(this);
    m_btn_about->setToolButtonStyle(Qt::ToolButtonStyle::ToolButtonTextBesideIcon);
    m_btn_about->setIcon(QIcon(":/Icon/about.png"));
    m_btn_about->setText("关于");
    ui->toolBar->addWidget(m_btn_about);

    // 模拟弹簧，分隔左右区域
    spacer = new QWidget(this);
    spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    ui->toolBar->addWidget(spacer);

    // 清除错误
    m_btn_clearError = new QToolButton(this);
    m_btn_clearError->setToolButtonStyle(Qt::ToolButtonStyle::ToolButtonTextBesideIcon);
    m_btn_clearError->setIcon(QIcon(":/Icon/clear.png"));
    m_btn_clearError->setText("清除错误");
    ui->toolBar->addWidget(m_btn_clearError);
    connect(m_btn_clearError, &QToolButton::clicked, this, &MainWindow::slot_BtnClearError_Clicked);

    // 添加串口号cbox控件
    label = new QLabel("串口号:");
    ui->toolBar->addWidget(label);
    m_cbox_serial = new QComboBox(this);
    ui->toolBar->addWidget(m_cbox_serial);

    // 宽度位10的间隔
    spacer = new QWidget(this);
    spacer->setMinimumWidth(10);
    ui->toolBar->addWidget(spacer);

    // 添加波特率cbox控件
    label = new QLabel("波特率:");
    ui->toolBar->addWidget(label);
    m_cbox_baudrate = new QComboBox(this);
    m_cbox_baudrate->setMinimumWidth(80);
    m_cbox_baudrate->setEditable(true);
    ui->toolBar->addWidget(m_cbox_baudrate);

    // 添加宽度为10的间隔
    spacer = new QWidget(this);
    spacer->setMinimumWidth(10);
    ui->toolBar->addWidget(spacer);

    // 添加连接按钮
    m_btn_connect = new QToolButton(this);
    m_btn_connect->setToolButtonStyle(Qt::ToolButtonStyle::ToolButtonTextBesideIcon);
    m_btn_connect->setIcon(QIcon(":/Icon/connect.png"));
    m_btn_connect->setText("连接");
    connect(m_btn_connect, &QToolButton::clicked, this, &MainWindow::slot_BtnConnect_Clicked); // 绑定连接按钮的信号槽
    ui->toolBar->addWidget(m_btn_connect);
}

void MainWindow::InitStateBar()
{
    QLabel* label = nullptr;
    QWidget* spacer = nullptr;

    // 设置statusbar属性
    ui->statusbar->setStyleSheet("QStatusBar::item{border: 0px}");
    ui->statusbar->setSizeGripEnabled(false);

    // 串口状态
    label = new QLabel("UART State:");
    label->setFont(QFont("宋体", 13, QFont::Bold));
    ui->statusbar->addWidget(label);
    m_label_serialState = new QLabel(this);
    m_label_serialState->setFont(QFont("宋体", 13, QFont::Bold));
    ui->statusbar->addWidget(m_label_serialState);

    // 发送错误
    label = new QLabel("TxErr:");
    label->setFont(QFont("宋体", 13, QFont::Bold));
    ui->statusbar->addWidget(label);
    m_label_txErr = new QLabel(this);
    m_label_txErr->setMinimumWidth(30);
    m_label_txErr->setFont(QFont("宋体", 13, QFont::Bold));
    ui->statusbar->addWidget(m_label_txErr);

    // 接收错误
    label = new QLabel("RxErr:");
    label->setFont(QFont("宋体", 13, QFont::Bold));
    ui->statusbar->addWidget(label);
    m_label_rxErr = new QLabel(this);
    m_label_rxErr->setMinimumWidth(30);
    m_label_rxErr->setFont(QFont("宋体", 13, QFont::Bold));
    ui->statusbar->addWidget(m_label_rxErr);

    // 永久信息栏
    m_label_systemState = new QLabel("motor_tool v0.1", this);
    m_label_systemState->setFont(QFont("宋体", 13, QFont::Bold));
    ui->statusbar->addPermanentWidget(m_label_systemState);
}

void MainWindow::InitDebugUI()
{
    QLabel* label = nullptr;
    QSpacerItem* spacer = nullptr;
    QHBoxLayout* subHBox = nullptr;
    QVBoxLayout* subVBox = nullptr;

    // 创建总体垂直布局
    QVBoxLayout* vbox = new QVBoxLayout();
    vbox->setMargin(0);

    QWidget* top = new QWidget();
    QWidget* mid = new QWidget();
    QWidget* down = new QWidget();
    down->setMaximumHeight(100); // 设置最下面区域的最大高度

    vbox->addWidget(top);
    vbox->addWidget(mid);
    vbox->addWidget(down);
    m_widget_debug->setLayout(vbox);

    // 创建top区域的内容，表头上方使用水平布局，然后整个和图表进行垂直布局
    if (true)
    {
        subHBox = new QHBoxLayout();
        subHBox->setMargin(0);

        // 游标
        m_label_debugPage_cursor1 = new QLabel("值:0");
        subHBox->addWidget(m_label_debugPage_cursor1);

        // 弹簧
        spacer = new QSpacerItem(10, 10, QSizePolicy::Expanding);
        subHBox->addSpacerItem(spacer);

        // 数据源下拉列表
        label = new QLabel("源:");
        subHBox->addWidget(label);
        m_cbox_debugPage_source1 = new QComboBox();
        m_cbox_debugPage_source1->addItem("实际转矩", DATA_TYPE_TORQUE);
        m_cbox_debugPage_source1->addItem("实际速度", DATA_TYPE_ENCODER_VEL);
        m_cbox_debugPage_source1->addItem("实际位置", DATA_TYPE_ENCODER_POS);
        m_cbox_debugPage_source1->addItem("电机电流", DATA_TYPE_IQ_FILT);
        m_cbox_debugPage_source1->addItem("总线电压", DATA_TYPE_VBUS_FILT);
        m_cbox_debugPage_source1->addItem("总线电流", DATA_TYPE_IBUS_FILT);
        m_cbox_debugPage_source1->addItem("输出功率", DATA_TYPE_POWER_FILT);
        subHBox->addWidget(m_cbox_debugPage_source1);

        // 采样点数下拉列表
        label = new QLabel("采样点数:");
        subHBox->addWidget(label);
        m_cbox_debugPage_sampleCount1 = new QComboBox();
        m_cbox_debugPage_sampleCount1->addItem("500", 500);
        m_cbox_debugPage_sampleCount1->addItem("1000", 1000);
        m_cbox_debugPage_sampleCount1->addItem("2000", 2000);
        m_cbox_debugPage_sampleCount1->addItem("4000", 4000);
        subHBox->addWidget(m_cbox_debugPage_sampleCount1);

        // 运行按钮
        m_btn_debugPage_scope1Work = new QToolButton();
        m_btn_debugPage_scope1Work->setIcon(QIcon(":/Icon/run.png"));
        connect(m_btn_debugPage_scope1Work, &QToolButton::clicked, this, &MainWindow::slot_BtnDebugPageScope1Work_Clicked);
        subHBox->addWidget(m_btn_debugPage_scope1Work);

        // 示波器
        m_qcp_debugPage_scope1 = new QCustomPlot();
        m_qcp_debugPage_scope1->xAxis->setRange(1, 4000);
        //m_qcp_debugPage_scope1->yAxis->setRange(-4, 4);
        m_qcp_debugPage_scope1->addGraph();
        m_qcp_debugPage_scope1->yAxis->rescale(true);
        m_qcp_debugPage_scope1->setInteraction(QCP::iRangeDrag, true); // 使能拖动
        m_qcp_debugPage_scope1->setInteraction(QCP::iRangeZoom, true); // 滚轮滑动缩放
        connect(m_qcp_debugPage_scope1, &QCustomPlot::mouseMove, this, &MainWindow::slot_QcpDebugPageScope_MouseMove); // 绑定鼠标移动信号槽
        connect(m_qcp_debugPage_scope1, &QCustomPlot::mouseRelease, this, &MainWindow::slot_QcpDebugPageScope_RightClicked); // 绑定鼠标右击信号槽
        // 游标
        m_qcpcursor_debugPageScope1 = new QCPItemTracer(m_qcp_debugPage_scope1);
        m_qcpcursor_debugPageScope1->setPen(QPen(Qt::red)); // 圆圈轮廓颜色
        //m_qcpcursor_debugPageScope1->setBrush(QBrush(Qt::red)); // 圆圈圈内颜色
        m_qcpcursor_debugPageScope1->setStyle(QCPItemTracer::tsCircle); // 圆圈
        m_qcpcursor_debugPageScope1->setSize(10); // 设置大小
        m_qcpcursor_debugPageScope1->setVisible(true);

        // 布局
        subVBox = new QVBoxLayout();
        subVBox->addLayout(subHBox);
        subVBox->addWidget(m_qcp_debugPage_scope1);
        top->setLayout(subVBox);
    }

    // 创建mid区域的内容，表头上方使用水平布局，然后整个和图表进行垂直布局
    if (true)
    {
        subHBox = new QHBoxLayout();
        subHBox->setMargin(0);

        m_label_debugPage_cursor2 = new QLabel("值:0");
        subHBox->addWidget(m_label_debugPage_cursor2);

        // 弹簧
        spacer = new QSpacerItem(10, 10, QSizePolicy::Expanding);
        subHBox->addSpacerItem(spacer);

        label = new QLabel("源:");
        subHBox->addWidget(label);
        m_cbox_debugPage_source2 = new QComboBox();
        m_cbox_debugPage_source2->addItem("实际转矩", DATA_TYPE_TORQUE);
        m_cbox_debugPage_source2->addItem("实际速度", DATA_TYPE_ENCODER_VEL);
        m_cbox_debugPage_source2->addItem("实际位置", DATA_TYPE_ENCODER_POS);
        m_cbox_debugPage_source2->addItem("电机电流", DATA_TYPE_IQ_FILT);
        m_cbox_debugPage_source2->addItem("总线电压", DATA_TYPE_VBUS_FILT);
        m_cbox_debugPage_source2->addItem("总线电流", DATA_TYPE_IBUS_FILT);
        m_cbox_debugPage_source2->addItem("输出功率", DATA_TYPE_POWER_FILT);
        subHBox->addWidget(m_cbox_debugPage_source2);

        // 采样点数下拉列表
        label = new QLabel("采样点数:");
        subHBox->addWidget(label);
        m_cbox_debugPage_sampleCount2 = new QComboBox();
        m_cbox_debugPage_sampleCount2->addItem("500", 500);
        m_cbox_debugPage_sampleCount2->addItem("1000", 1000);
        m_cbox_debugPage_sampleCount2->addItem("2000", 2000);
        m_cbox_debugPage_sampleCount2->addItem("4000", 4000);
        subHBox->addWidget(m_cbox_debugPage_sampleCount2);

        m_btn_debugPage_scope2Work = new QToolButton();
        m_btn_debugPage_scope2Work->setIcon(QIcon(":/Icon/run.png"));
        connect(m_btn_debugPage_scope2Work, &QToolButton::clicked, this, &MainWindow::slot_BtnDebugPageScope2Work_Clicked);
        subHBox->addWidget(m_btn_debugPage_scope2Work);

        // 示波器
        m_qcp_debugPage_scope2 = new QCustomPlot();
        m_qcp_debugPage_scope2->xAxis->setRange(1, 4000);
        //m_qcp_debugPage_scope2->yAxis->setRange(-4, 4);
        m_qcp_debugPage_scope2->addGraph();
        m_qcp_debugPage_scope2->yAxis->rescale(true);
        m_qcp_debugPage_scope2->setInteraction(QCP::iRangeDrag, true); // 使能拖动
        m_qcp_debugPage_scope2->setInteraction(QCP::iRangeZoom, true); // 滚轮滑动缩放
        connect(m_qcp_debugPage_scope2, &QCustomPlot::mouseMove, this, &MainWindow::slot_QcpDebugPageScope_MouseMove); // 绑定鼠标移动信号槽
        connect(m_qcp_debugPage_scope2, &QCustomPlot::mouseRelease, this, &MainWindow::slot_QcpDebugPageScope_RightClicked); // 绑定鼠标右击信号槽
        // 游标
        m_qcpcursor_debugPageScope2 = new QCPItemTracer(m_qcp_debugPage_scope2);
        m_qcpcursor_debugPageScope2->setPen(QPen(Qt::red)); // 圆圈轮廓颜色
        m_qcpcursor_debugPageScope2->setStyle(QCPItemTracer::tsCircle); // 圆圈
        m_qcpcursor_debugPageScope2->setSize(10); // 设置大小
        m_qcpcursor_debugPageScope2->setVisible(true);

        // 布局
        subVBox = new QVBoxLayout();
        subVBox->addLayout(subHBox);
        subVBox->addWidget(m_qcp_debugPage_scope2);

        // 设置mid区域布局
        mid->setLayout(subVBox);
    }

    // 创建down区域的内容，每行使用水平布局，整体使用垂直布局
    if (true)
    {
        subVBox = new QVBoxLayout();
        subVBox->setMargin(0);

        ////////////////////////////  第一行 ////////////////////////////
        subHBox = new QHBoxLayout();

        // 状态信息
        label = new QLabel("状态信息:");
        subHBox->addWidget(label);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding));

        // 转矩
        label = new QLabel("转矩(Nm):");
        subHBox->addWidget(label);
        m_lineEdit_debugPage_torque = new QLineEdit();
        m_lineEdit_debugPage_torque->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
        m_lineEdit_debugPage_torque->setText("0.0");
        m_lineEdit_debugPage_torque->setMaximumWidth(100);
        subHBox->addWidget(m_lineEdit_debugPage_torque);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Maximum));
        m_btn_debugPage_sendTorque = new QPushButton("发送");
        connect(m_btn_debugPage_sendTorque, &QPushButton::clicked, this, &MainWindow::slot_BtnDebugPageSetTorque_Clicked);
        subHBox->addWidget(m_btn_debugPage_sendTorque);

        // 启动电机
        m_btn_debugPage_runMotor = new QPushButton("启动电机");
        connect(m_btn_debugPage_runMotor, &QPushButton::clicked, this, &MainWindow::slot_BtnDebugPageStartMotor_Clicked);
        subHBox->addWidget(m_btn_debugPage_runMotor);

        subVBox->addLayout(subHBox); // 添加第一行水平布局

        ////////////////////////////  第二行 ////////////////////////////
        subHBox = new QHBoxLayout();

        // 电机使能指示灯
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Maximum));
        m_label_motorEnable = new QLabel();
        m_label_motorEnable->setScaledContents(true);
        m_label_motorEnable->setMinimumSize(QSize(15, 15));
        m_label_motorEnable->setMaximumSize(QSize(15, 15));
        subHBox->addWidget(m_label_motorEnable);
        label = new QLabel("电机使能");
        subHBox->addWidget(label);
        subHBox->addSpacerItem(new QSpacerItem(20, 10, QSizePolicy::Maximum));

        // 目标到达指示灯
        m_label_targetReached = new QLabel();
        m_label_targetReached->setScaledContents(true);
        m_label_targetReached->setMinimumSize(QSize(15, 15));
        m_label_targetReached->setMaximumSize(QSize(15, 15));
        subHBox->addWidget(m_label_targetReached);
        label = new QLabel("目标到达");
        subHBox->addWidget(label);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding));

        // 转速
        label = new QLabel("速度(Turn/s):");
        subHBox->addWidget(label);
        m_lineEdit_debugPage_vel = new QLineEdit();
        m_lineEdit_debugPage_vel->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
        m_lineEdit_debugPage_vel->setText("0.0");
        m_lineEdit_debugPage_vel->setMaximumWidth(100);
        subHBox->addWidget(m_lineEdit_debugPage_vel);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Maximum));
        m_btn_debugPage_sendVel = new QPushButton("发送");
        connect(m_btn_debugPage_sendVel, &QPushButton::clicked, this, &MainWindow::slot_BtnDebugPageSetVel_Clicked);
        subHBox->addWidget(m_btn_debugPage_sendVel);

        // 停止电机
        m_btn_debugPage_stopMotor = new QPushButton("停止电机");
        connect(m_btn_debugPage_stopMotor, &QPushButton::clicked, this, &MainWindow::slot_BtnDebugPageStopMotor_Clicked);
        subHBox->addWidget(m_btn_debugPage_stopMotor);

        subVBox->addLayout(subHBox); // 添加第二行水平布局

        ////////////////////////////  第三行 ////////////////////////////
        subHBox = new QHBoxLayout();

        // 电流限制指示灯
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Maximum));
        m_label_currentLimitActive = new QLabel();
        m_label_currentLimitActive->setScaledContents(true);
        m_label_currentLimitActive->setMinimumSize(QSize(15, 15));
        m_label_currentLimitActive->setMaximumSize(QSize(15, 15));
        subHBox->addWidget(m_label_currentLimitActive);
        label = new QLabel("电流限制");
        subHBox->addWidget(label);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding));

        // 位置
        label = new QLabel("位置(Turn):");
        subHBox->addWidget(label);
        m_lineEdit_debugPage_pos = new QLineEdit();
        m_lineEdit_debugPage_pos->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
        m_lineEdit_debugPage_pos->setText("0.0");
        m_lineEdit_debugPage_pos->setMaximumWidth(100);
        subHBox->addWidget(m_lineEdit_debugPage_pos);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Maximum));
        m_btn_debugPage_sendPos = new QPushButton("发送");
        connect(m_btn_debugPage_sendPos, &QPushButton::clicked, this, &MainWindow::slot_BtnDebugPageSetPos_Clicked);
        subHBox->addWidget(m_btn_debugPage_sendPos);

        // 复位原点
        m_btn_debugPage_resetHome = new QPushButton("复位原点");
        connect(m_btn_debugPage_resetHome, &QPushButton::clicked, this, &MainWindow::slot_BtnDebugPageResetHome_Clicked);
        subHBox->addWidget(m_btn_debugPage_resetHome);

        subVBox->addLayout(subHBox); // 添加第三行水平布局

        // down区域整体使用垂直布局
        down->setLayout(subVBox);
    }
}

void MainWindow::InitCalibUI()
{
    QLabel* label = nullptr;
    QHBoxLayout* subHBox = nullptr;
    QVBoxLayout* subVBox = nullptr;
    QWidget* widget = nullptr;

    // 创建总体垂直布局
    QVBoxLayout* vbox = new QVBoxLayout();
    vbox->setMargin(0);

    QWidget* top = new QWidget();
    QWidget* down = new QWidget();

    vbox->addWidget(top);
    vbox->addWidget(down);
    m_widget_calib->setLayout(vbox);

    ///
    /// 创建电机和编码器校准区域
    /// 1. 右侧数据显示区域放进一个widget中，使用先水平布局，后垂直布局
    /// 2. 与左侧图表水平布局
    /// 3. 整体在再与上方标签垂直布局，并设置标签最大高度
    ///
    if (true)
    {
        subVBox = new QVBoxLayout();

        subHBox = new QHBoxLayout();
        label = new QLabel("电机相电阻(R):");
        subHBox->addWidget(label);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding));
        m_label_calibPage_motorR = new QLabel("NULL");
        subHBox->addWidget(m_label_calibPage_motorR);
        subVBox->addLayout(subHBox); // 添加第一行水平布局

        subHBox = new QHBoxLayout();
        label = new QLabel("电机相电感(R):");
        subHBox->addWidget(label);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding));
        m_label_calibPage_motorL = new QLabel("NULL");
        subHBox->addWidget(m_label_calibPage_motorL);
        subVBox->addLayout(subHBox); // 添加第二行水平布局

        subHBox = new QHBoxLayout();
        label = new QLabel("电机极对数:");
        subHBox->addWidget(label);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding));
        m_label_calibPage_motorPP = new QLabel("NULL");
        subHBox->addWidget(m_label_calibPage_motorPP);
        subVBox->addLayout(subHBox); // 添加第三行水平布局

        subHBox = new QHBoxLayout();
        label = new QLabel("编码器方向:");
        subHBox->addWidget(label);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding));
        m_label_calibPage_encoderDir = new QLabel("NULL");
        subHBox->addWidget(m_label_calibPage_encoderDir);
        subVBox->addLayout(subHBox); // 添加第四行水平布局

        subHBox = new QHBoxLayout();
        label = new QLabel("编码器偏移:");
        subHBox->addWidget(label);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding));
        m_label_calibPage_encoderOffset = new QLabel("NULL");
        subHBox->addWidget(m_label_calibPage_encoderOffset);
        subVBox->addLayout(subHBox); // 添加第五行水平布局

        subHBox = new QHBoxLayout();
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding));
        m_btn_calibPage_startMotorAndEncoderCalib = new QPushButton("开始校准");
        m_btn_calibPage_startMotorAndEncoderCalib->setMinimumHeight(30);
        connect(m_btn_calibPage_startMotorAndEncoderCalib, &QPushButton::clicked, this, &MainWindow::slot_BtnCalibPageStartCalibMotorAndEncoder_Clicked);
        subHBox->addWidget(m_btn_calibPage_startMotorAndEncoderCalib);
        subVBox->addLayout(subHBox); // 添加第六行水平布局

        // 右侧区域放到一个小widget里面布局
        widget = new QWidget();
        widget->setMaximumWidth(220);
        widget->setLayout(subVBox);

        // 示波器与右侧区域水平布局
        subHBox = new QHBoxLayout();
        m_qcp_calibPage_encoderCalib = new QCustomPlot();
        m_qcp_calibPage_encoderCalib->xAxis->setRange(0, OFFSET_LUT_NUM);
        m_qcp_calibPage_encoderCalib->yAxis->rescale(true);
        m_qcp_calibPage_encoderCalib->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored);
        subHBox->addWidget(m_qcp_calibPage_encoderCalib);
        subHBox->addWidget(widget);

        // 添加上方标题
        subVBox = new QVBoxLayout();
        subVBox->setMargin(0);
        subVBox->setSpacing(0);

        label = new QLabel("电机和编码器校准:");
        label->setMaximumHeight(30);
        subVBox->addWidget(label);
        subVBox->addLayout(subHBox);

        top->setLayout(subVBox); // top区域设置布局
    }

    ///
    /// 创建齿槽转矩校准区域
    /// 1. 最下面按钮左侧加弹簧，然后从上到下垂直布局
    ///
    if (true)
    {
        subVBox = new QVBoxLayout();
        subVBox->setMargin(0);
        subVBox->setSpacing(0);

        label = new QLabel("齿槽转矩校准:");
        label->setMaximumHeight(30);
        subVBox->addWidget(label);

        // 示波器
        m_qcp_calibPage_anticogCalib = new QCustomPlot();
        m_qcp_calibPage_anticogCalib->xAxis->setRange(0, COGGING_MAP_NUM);
        m_qcp_calibPage_anticogCalib->yAxis->rescale(true);
        m_qcp_calibPage_anticogCalib->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored);
        subVBox->addWidget(m_qcp_calibPage_anticogCalib);

        subHBox = new QHBoxLayout();
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding));

        m_btn_calibPage_startAnticogCalib = new QPushButton("开始校准");
        m_btn_calibPage_startAnticogCalib->setMaximumHeight(30);
        connect(m_btn_calibPage_startAnticogCalib, &QPushButton::clicked, this, &MainWindow::slot_BtnCalibPageStartCalibAnticogging_Clicked);
        subHBox->addWidget(m_btn_calibPage_startAnticogCalib);

        // 添加弹簧，让最下面按钮和上面示波器有一点间隔
        subVBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Maximum));
        subVBox->addLayout(subHBox);

        down->setLayout(subVBox); // down区域设置布局
    }
}

void MainWindow::InitConfigUI()
{
    QGridLayout* gridBox = nullptr;
    QHBoxLayout* subHBox = nullptr;
    QVBoxLayout* subVBox = nullptr;
    QWidget* widget = nullptr;
    QLabel* label = nullptr;
    QFrame* line = nullptr;

    ///
    /// 配置项及其对应的索引
    /// 单片机程序中做了减一处理，所以索引从1开始
    /// int32_t idx = data_to_int32(data) - 1; // 0~32
    ///
    if (true)
    {
        int configIndex = 1;
        m_map_configToIndex = new QMap<QString, int>;
        m_map_configToIndex->insert("invertDir", configIndex++);
        m_map_configToIndex->insert("inertia", configIndex++);
        m_map_configToIndex->insert("torqueConstant", configIndex++);
        m_map_configToIndex->insert("motorPP", configIndex++);
        m_map_configToIndex->insert("motorR", configIndex++);
        m_map_configToIndex->insert("motorL", configIndex++);
        m_map_configToIndex->insert("motorCurrentLimit", configIndex++);
        m_map_configToIndex->insert("motorVelLimit", configIndex++);
        m_map_configToIndex->insert("calibCurrent", configIndex++);
        m_map_configToIndex->insert("calibVoltage", configIndex++);
        m_map_configToIndex->insert("ctrlMode", configIndex++);
        m_map_configToIndex->insert("posGain", configIndex++);
        m_map_configToIndex->insert("velGain", configIndex++);
        m_map_configToIndex->insert("velIntegratorGain", configIndex++);
        m_map_configToIndex->insert("currentCtrlBW", configIndex++);
        m_map_configToIndex->insert("anticoggingEnable", configIndex++);
        m_map_configToIndex->insert("syncTargetEnable", configIndex++);
        m_map_configToIndex->insert("targetVelWindow", configIndex++);
        m_map_configToIndex->insert("targetPosWindow", configIndex++);
        m_map_configToIndex->insert("torqueRampRate", configIndex++);
        m_map_configToIndex->insert("velRampRate", configIndex++);
        m_map_configToIndex->insert("posFilterBW", configIndex++);
        m_map_configToIndex->insert("profileVel", configIndex++);
        m_map_configToIndex->insert("profileAccel", configIndex++);
        m_map_configToIndex->insert("profileDecel", configIndex++);
        m_map_configToIndex->insert("protectUnderVolt", configIndex++);
        m_map_configToIndex->insert("protectOverVolt", configIndex++);
        m_map_configToIndex->insert("protectOverCurr", configIndex++);
        m_map_configToIndex->insert("protectIBusMax", configIndex++);
    }

    // 配置项索引对应配置项
    if (true)
    {
        int configIndex = 1;
        m_map_indexToConfig = new QMap<int, QString>;
        m_map_indexToConfig->insert(configIndex++, "invertDir");
        m_map_indexToConfig->insert(configIndex++, "inertia");
        m_map_indexToConfig->insert(configIndex++, "torqueConstant");
        m_map_indexToConfig->insert(configIndex++, "motorPP");
        m_map_indexToConfig->insert(configIndex++, "motorR");
        m_map_indexToConfig->insert(configIndex++, "motorL");
        m_map_indexToConfig->insert(configIndex++, "motorCurrentLimit");
        m_map_indexToConfig->insert(configIndex++, "motorVelLimit");
        m_map_indexToConfig->insert(configIndex++, "calibCurrent");
        m_map_indexToConfig->insert(configIndex++, "calibVoltage");
        m_map_indexToConfig->insert(configIndex++, "ctrlMode");
        m_map_indexToConfig->insert(configIndex++, "posGain");
        m_map_indexToConfig->insert(configIndex++, "velGain");
        m_map_indexToConfig->insert(configIndex++, "velIntegratorGain");
        m_map_indexToConfig->insert(configIndex++, "currentCtrlBW");
        m_map_indexToConfig->insert(configIndex++, "anticoggingEnable");
        m_map_indexToConfig->insert(configIndex++, "syncTargetEnable");
        m_map_indexToConfig->insert(configIndex++, "targetVelWindow");
        m_map_indexToConfig->insert(configIndex++, "targetPosWindow");
        m_map_indexToConfig->insert(configIndex++, "torqueRampRate");
        m_map_indexToConfig->insert(configIndex++, "velRampRate");
        m_map_indexToConfig->insert(configIndex++, "posFilterBW");
        m_map_indexToConfig->insert(configIndex++, "profileVel");
        m_map_indexToConfig->insert(configIndex++, "profileAccel");
        m_map_indexToConfig->insert(configIndex++, "profileDecel");
        m_map_indexToConfig->insert(configIndex++, "protectUnderVolt");
        m_map_indexToConfig->insert(configIndex++, "protectOverVolt");
        m_map_indexToConfig->insert(configIndex++, "protectOverCurr");
        m_map_indexToConfig->insert(configIndex++, "protectIBusMax");
    }

    // 配置项的数据类型
    if (true)
    {
        m_map_configToDataType = new QMap<QString, int>;
        m_map_configToDataType->insert("invertDir", CONFIG_DATA_TYPE_INT32);
        m_map_configToDataType->insert("inertia", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("torqueConstant", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("motorPP", CONFIG_DATA_TYPE_INT32);
        m_map_configToDataType->insert("motorR", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("motorL", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("motorCurrentLimit", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("motorVelLimit", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("calibCurrent", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("calibVoltage", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("ctrlMode", CONFIG_DATA_TYPE_INT32);
        m_map_configToDataType->insert("posGain", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("velGain", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("velIntegratorGain", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("currentCtrlBW", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("anticoggingEnable", CONFIG_DATA_TYPE_INT32);
        m_map_configToDataType->insert("syncTargetEnable", CONFIG_DATA_TYPE_INT32);
        m_map_configToDataType->insert("targetVelWindow", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("targetPosWindow", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("torqueRampRate", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("velRampRate", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("posFilterBW", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("profileVel", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("profileAccel", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("profileDecel", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("protectUnderVolt", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("protectOverVolt", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("protectOverCurr", CONFIG_DATA_TYPE_FLOAT);
        m_map_configToDataType->insert("protectIBusMax", CONFIG_DATA_TYPE_FLOAT);
    }

    ///
    /// 实现“配置”界面的读取所有和写入所有
    /// 1. 存放所有的read button
    /// 2. 存放所有的write button
    /// 3. 读取所有操作依次调用所有read button
    /// 4. 写入所有操作依次调用所有write button
    ///
    m_vector_allReadCfgBtn = new QVector<QPushButton*>;
    m_vector_allWriteCfgBtn = new QVector<QPushButton*>;

    ///
    /// 上面区域用QScrollArea。下面按钮区域用widget，高度固定。
    /// 上下两部分垂直布局。
    ///
    subVBox = new QVBoxLayout();
    subVBox->setMargin(0);

    // 整体垂直布局
    m_widget_config->setLayout(subVBox);

    ///
    /// 上面配置选项区域使用栅格布局，一行一行往下排列
    /// (1)new QscrollArea
    /// (2)new 内部的暮布容器
    /// (3)new 布局,例如网格布局QGridLayout(前3步不分先后顺序)或者你想用的其他布局
    /// (4)向布局中添加你想要的控件(这一步必须位于步骤3之后，这不是废话吗)
    /// (5)关联"墓布控件"和"布局"(如果在创建布局时，就把布局构造在了幕布控件中，那么这一步就省了)
    /// (6)给QScro0lArea设置幕布，也即调用QScrollArea.setWidget(QWidget*)，这一步必须位于步骤4、5之后
    ///
    if (true)
    {
        // 创建QScrollArea，添加到“配置”界面的垂直布局中
        QScrollArea* scrollArea = new QScrollArea(m_widget_config);
        subVBox->addWidget(scrollArea);

        // 创建QScrollArea的幕布
        QWidget* scrollAreaWidgetContents = new QWidget();

        // 栅格布局
        gridBox = new QGridLayout(scrollAreaWidgetContents);

        int row = 0, col = 0;
        const int unitLabelWidth = 60;

        ///////////////////////////// 电机 /////////////////////////////
        label = new QLabel("电机:");
        label->setMaximumHeight(30);
        gridBox->addWidget(label, row, 0, 1, -1);
        // 分割线
        line = new QFrame;
        line->setFrameShape(QFrame::HLine);
        line->setFrameShadow(QFrame::Plain);
        gridBox->addWidget(line, ++row, 0, 1, -1);

        // 01 [I32]反转方向
        if (true)
        {
            row++; col = 0;
            label = new QLabel("01 [I32]反转方向", scrollAreaWidgetContents);
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_cbox_configPage_invertDir = new QComboBox(scrollAreaWidgetContents);
            m_cbox_configPage_invertDir->setMaximumWidth(130);
            m_cbox_configPage_invertDir->addItem("否", false);
            m_cbox_configPage_invertDir->addItem("是", true);
            gridBox->addWidget(m_cbox_configPage_invertDir, row, col++);

            label = new QLabel(scrollAreaWidgetContents);
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_invertDir = new QPushButton(scrollAreaWidgetContents); // 读取
            m_btn_configPage_read_invertDir->setMaximumSize(30, 30);
            m_btn_configPage_read_invertDir->setObjectName("read_invertDir");
            m_btn_configPage_read_invertDir->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_invertDir->setToolTip("读取配置");
            connect(m_btn_configPage_read_invertDir, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_invertDir); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_invertDir, row, col++);

            m_btn_configPage_write_invertDir = new QPushButton(scrollAreaWidgetContents); // 写入
            m_btn_configPage_write_invertDir->setMaximumSize(30, 30);
            m_btn_configPage_write_invertDir->setObjectName("write_invertDir");
            m_btn_configPage_write_invertDir->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_invertDir->setToolTip("写入配置");
            connect(m_btn_configPage_write_invertDir, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allWriteCfgBtn->append(m_btn_configPage_write_invertDir); // 将此写入按钮添加到容器
            gridBox->addWidget(m_btn_configPage_write_invertDir, row, col++);

            label = new QLabel("是否反转电机旋转方向");
            gridBox->addWidget(label, row, col++);
        }

        // 02 [F32]转动惯量
        if (true)
        {
            row++; col = 0;
            label = new QLabel("02 [F32]转动惯量");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_inertia = new QLineEdit;
            m_lineEdit_configPage_inertia->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_inertia->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_inertia, row, col++);

            label = new QLabel;
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_inertia = new QPushButton; // 读取
            m_btn_configPage_read_inertia->setMaximumSize(30, 30);
            m_btn_configPage_read_inertia->setObjectName("read_inertia");
            m_btn_configPage_read_inertia->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_inertia->setToolTip("读取配置");
            connect(m_btn_configPage_read_inertia, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_inertia); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_inertia, row, col++);

            m_btn_configPage_write_inertia = new QPushButton; // 写入
            m_btn_configPage_write_inertia->setMaximumSize(30, 30);
            m_btn_configPage_write_inertia->setObjectName("write_inertia");
            m_btn_configPage_write_inertia->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_inertia->setToolTip("写入配置");
            connect(m_btn_configPage_write_inertia, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_inertia, row, col++);

            label = new QLabel("电机转子和负载的转动惯量，单位为[A/(turn/s^2)]，取值范围(0~100)");
            gridBox->addWidget(label, row, col++);
        }

        // 03 [F32]转矩常数
        if (true)
        {
            row++; col = 0;
            label = new QLabel("03 [F32]转矩常数");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_torqueConstant = new QLineEdit;
            m_lineEdit_configPage_torqueConstant->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_torqueConstant->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_torqueConstant, row, col++);

            label = new QLabel("[Nm/A]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_torqueConstant = new QPushButton; // 读取
            m_btn_configPage_read_torqueConstant->setMaximumSize(30, 30);
            m_btn_configPage_read_torqueConstant->setObjectName("read_torqueConstant");
            m_btn_configPage_read_torqueConstant->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_torqueConstant->setToolTip("读取配置");
            connect(m_btn_configPage_read_torqueConstant, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_torqueConstant); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_torqueConstant, row, col++);

            m_btn_configPage_write_torqueConstant = new QPushButton; // 写入
            m_btn_configPage_write_torqueConstant->setMaximumSize(30, 30);
            m_btn_configPage_write_torqueConstant->setObjectName("write_torqueConstant");
            m_btn_configPage_write_torqueConstant->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_torqueConstant->setToolTip("写入配置");
            connect(m_btn_configPage_write_torqueConstant, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_torqueConstant, row, col++);

            label = new QLabel("电机转矩常数，取值范围(0~10)");
            gridBox->addWidget(label, row, col++);
        }

        // 04 [I32]电机磁极对数
        if (true)
        {
            row++; col = 0;
            label = new QLabel("04 [I32]电机磁极对数");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_motorPP = new QLineEdit;
            m_lineEdit_configPage_motorPP->setValidator(new QIntValidator(m_lineEdit_configPage_torqueConstant)); // 限制只能输入整形
            m_lineEdit_configPage_motorPP->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_motorPP, row, col++);

            label = new QLabel("[PP]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_motorPP = new QPushButton; // 读取
            m_btn_configPage_read_motorPP->setMaximumSize(30, 30);
            m_btn_configPage_read_motorPP->setObjectName("read_motorPP");
            m_btn_configPage_read_motorPP->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_motorPP->setToolTip("读取配置");
            connect(m_btn_configPage_read_motorPP, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_motorPP); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_motorPP, row, col++);

            m_btn_configPage_write_motorPP = new QPushButton; // 写入
            m_btn_configPage_write_motorPP->setMaximumSize(30, 30);
            m_btn_configPage_write_motorPP->setObjectName("write_motorPP");
            m_btn_configPage_write_motorPP->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_motorPP->setToolTip("写入配置");
            connect(m_btn_configPage_write_motorPP, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_motorPP, row, col++);

            label = new QLabel("电机磁极对数，校准过程自动测量，取值范围(2~30)");
            gridBox->addWidget(label, row, col++);
        }

        // 05 [F32]电机相电阻
        if (true)
        {
            row++; col = 0;
            label = new QLabel("05 [F32]电机相电阻");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_motorR = new QLineEdit;
            m_lineEdit_configPage_motorR->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_motorR->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_motorR, row, col++);

            label = new QLabel("[R]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_motorR = new QPushButton; // 读取
            m_btn_configPage_read_motorR->setMaximumSize(30, 30);
            m_btn_configPage_read_motorR->setObjectName("read_motorR");
            m_btn_configPage_read_motorR->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_motorR->setToolTip("读取配置");
            connect(m_btn_configPage_read_motorR, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_motorR); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_motorR, row, col++);

            m_btn_configPage_write_motorR = new QPushButton; // 写入
            m_btn_configPage_write_motorR->setMaximumSize(30, 30);
            m_btn_configPage_write_motorR->setObjectName("write_motorR");
            m_btn_configPage_write_motorR->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_motorR->setToolTip("写入配置");
            connect(m_btn_configPage_write_motorR, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_motorR, row, col++);

            label = new QLabel("电机相电阻，校准过程自动测量，取值范围(0~10)");
            gridBox->addWidget(label, row, col++);
        }

        // 06 [F32]电机相电感
        if (true)
        {
            row++; col = 0;
            label = new QLabel("06 [F32]电机相电感");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_motorL = new QLineEdit;
            m_lineEdit_configPage_motorL->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_motorL->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_motorL, row, col++);

            label = new QLabel("[H]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_motorL = new QPushButton; // 读取
            m_btn_configPage_read_motorL->setMaximumSize(30, 30);
            m_btn_configPage_read_motorL->setObjectName("read_motorL");
            m_btn_configPage_read_motorL->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_motorL->setToolTip("读取配置");
            connect(m_btn_configPage_read_motorL, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_motorL); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_motorL, row, col++);

            m_btn_configPage_write_motorL = new QPushButton; // 写入
            m_btn_configPage_write_motorL->setMaximumSize(30, 30);
            m_btn_configPage_write_motorL->setObjectName("write_motorL");
            m_btn_configPage_write_motorL->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_motorL->setToolTip("写入配置");
            connect(m_btn_configPage_write_motorL, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_motorL, row, col++);

            label = new QLabel("电机相电感，校准过程自动测量，取值范围(0~10)");
            gridBox->addWidget(label, row, col++);
        }

        // 07 [F32]电机电流限制
        if (true)
        {
            row++; col = 0;
            label = new QLabel("07 [F32]电机电流限制");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_motorCurrentLimit = new QLineEdit;
            m_lineEdit_configPage_motorCurrentLimit->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_motorCurrentLimit->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_motorCurrentLimit, row, col++);

            label = new QLabel("[A]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_motorCurrentLimit = new QPushButton; // 读取
            m_btn_configPage_read_motorCurrentLimit->setMaximumSize(30, 30);
            m_btn_configPage_read_motorCurrentLimit->setObjectName("read_motorCurrentLimit");
            m_btn_configPage_read_motorCurrentLimit->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_motorCurrentLimit->setToolTip("读取配置");
            connect(m_btn_configPage_read_motorCurrentLimit, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_motorCurrentLimit); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_motorCurrentLimit, row, col++);

            m_btn_configPage_write_motorCurrentLimit = new QPushButton; // 写入
            m_btn_configPage_write_motorCurrentLimit->setMaximumSize(30, 30);
            m_btn_configPage_write_motorCurrentLimit->setObjectName("write_motorCurrentLimit");
            m_btn_configPage_write_motorCurrentLimit->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_motorCurrentLimit->setToolTip("写入配置");
            connect(m_btn_configPage_write_motorCurrentLimit, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_motorCurrentLimit, row, col++);

            label = new QLabel("电机最大电流限制，取值范围(0~10)");
            gridBox->addWidget(label, row, col++);
        }

        // 08 [F32]电机转速限制
        if (true)
        {
            row++; col = 0;
            label = new QLabel("08 [F32]电机转速限制");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_motorVelLimit = new QLineEdit;
            m_lineEdit_configPage_motorVelLimit->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_motorVelLimit->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_motorVelLimit, row, col++);

            label = new QLabel("[turn/s]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_motorVelLimit = new QPushButton; // 读取
            m_btn_configPage_read_motorVelLimit->setMaximumSize(30, 30);
            m_btn_configPage_read_motorVelLimit->setObjectName("read_motorVelLimit");
            m_btn_configPage_read_motorVelLimit->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_motorVelLimit->setToolTip("读取配置");
            connect(m_btn_configPage_read_motorVelLimit, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_motorVelLimit); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_motorVelLimit, row, col++);

            m_btn_configPage_write_motorVelLimit = new QPushButton; // 写入
            m_btn_configPage_write_motorVelLimit->setMaximumSize(30, 30);
            m_btn_configPage_write_motorVelLimit->setObjectName("write_motorVelLimit");
            m_btn_configPage_write_motorVelLimit->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_motorVelLimit->setToolTip("写入配置");
            connect(m_btn_configPage_write_motorVelLimit, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_motorVelLimit, row, col++);

            label = new QLabel("电机最大转速限制，取值范围(0~100)");
            gridBox->addWidget(label, row, col++);
        }

        ///////////////////////////// 校准 /////////////////////////////
        label = new QLabel("校准:");
        label->setMaximumHeight(30);
        gridBox->addWidget(label, ++row, 0, 1, -1);
        // 分割线
        line = new QFrame;
        line->setFrameShape(QFrame::HLine);
        line->setFrameShadow(QFrame::Plain);
        gridBox->addWidget(line, ++row, 0, 1, -1);

        // 09 [F32]校准电流
        if (true)
        {
            row++; col = 0;
            label = new QLabel("09 [F32]校准电流");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_calibCurrent = new QLineEdit;
            m_lineEdit_configPage_calibCurrent->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_calibCurrent->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_calibCurrent, row, col++);

            label = new QLabel("[A]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_calibCurrent = new QPushButton; // 读取
            m_btn_configPage_read_calibCurrent->setMaximumSize(30, 30);
            m_btn_configPage_read_calibCurrent->setObjectName("read_calibCurrent");
            m_btn_configPage_read_calibCurrent->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_calibCurrent->setToolTip("读取配置");
            connect(m_btn_configPage_read_calibCurrent, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_calibCurrent); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_calibCurrent, row, col++);

            m_btn_configPage_write_calibCurrent = new QPushButton; // 写入
            m_btn_configPage_write_calibCurrent->setMaximumSize(30, 30);
            m_btn_configPage_write_calibCurrent->setObjectName("write_calibCurrent");
            m_btn_configPage_write_calibCurrent->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_calibCurrent->setToolTip("写入配置");
            connect(m_btn_configPage_write_calibCurrent, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_calibCurrent, row, col++);

            label = new QLabel("校准时流过电机的电流，取值范围(0~10)");
            gridBox->addWidget(label, row, col++);
        }

        // 10 [F32]校准电压
        if (true)
        {
            row++; col = 0;
            label = new QLabel("10 [F32]校准电压");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_calibVoltage = new QLineEdit;
            m_lineEdit_configPage_calibVoltage->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_calibVoltage->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_calibVoltage, row, col++);

            label = new QLabel("[V]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_calibVoltage = new QPushButton; // 读取
            m_btn_configPage_read_calibVoltage->setMaximumSize(30, 30);
            m_btn_configPage_read_calibVoltage->setObjectName("read_calibVoltage");
            m_btn_configPage_read_calibVoltage->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_calibVoltage->setToolTip("读取配置");
            connect(m_btn_configPage_read_calibVoltage, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_calibVoltage); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_calibVoltage, row, col++);

            m_btn_configPage_write_calibVoltage = new QPushButton; // 写入
            m_btn_configPage_write_calibVoltage->setMaximumSize(30, 30);
            m_btn_configPage_write_calibVoltage->setObjectName("write_calibVoltage");
            m_btn_configPage_write_calibVoltage->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_calibVoltage->setToolTip("写入配置");
            connect(m_btn_configPage_write_calibVoltage, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_calibVoltage, row, col++);

            label = new QLabel("电机参数校准时的电压，取值范围(0~10)");
            gridBox->addWidget(label, row, col++);
        }

        ///////////////////////////// 控制器 /////////////////////////////
        label = new QLabel("控制器:");
        label->setMaximumHeight(30);
        gridBox->addWidget(label, ++row, 0, 1, -1);
        // 分割线
        line = new QFrame;
        line->setFrameShape(QFrame::HLine);
        line->setFrameShadow(QFrame::Plain);
        gridBox->addWidget(line, ++row, 0, 1, -1);

        // 11 [ENM]控制模式
        if (true)
        {
            row++; col = 0;
            label = new QLabel("11 [ENM]控制模式");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_cbox_configPage_ctrlMode = new QComboBox;
            m_cbox_configPage_ctrlMode->addItem("转矩爬升", CONTROL_MODE_TORQUE_RAMP);
            m_cbox_configPage_ctrlMode->addItem("转速爬升", CONTROL_MODE_VELOCITY_RAMP);
            m_cbox_configPage_ctrlMode->addItem("位置过滤", CONTROL_MODE_POSITION_FILTER);
            m_cbox_configPage_ctrlMode->addItem("轮廓位置", CONTROL_MODE_POSITION_PROFILE);
            m_cbox_configPage_ctrlMode->setMaximumWidth(130);
            gridBox->addWidget(m_cbox_configPage_ctrlMode, row, col++);

            label = new QLabel("");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_ctrlMode = new QPushButton; // 读取
            m_btn_configPage_read_ctrlMode->setMaximumSize(30, 30);
            m_btn_configPage_read_ctrlMode->setObjectName("read_ctrlMode");
            m_btn_configPage_read_ctrlMode->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_ctrlMode->setToolTip("读取配置");
            connect(m_btn_configPage_read_ctrlMode, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_ctrlMode); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_ctrlMode, row, col++);

            m_btn_configPage_write_ctrlMode = new QPushButton; // 写入
            m_btn_configPage_write_ctrlMode->setMaximumSize(30, 30);
            m_btn_configPage_write_ctrlMode->setObjectName("write_ctrlMode");
            m_btn_configPage_write_ctrlMode->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_ctrlMode->setToolTip("写入配置");
            connect(m_btn_configPage_write_ctrlMode, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_ctrlMode, row, col++);

            label = new QLabel("控制模式");
            gridBox->addWidget(label, row, col++);
        }

        // 12 [F32]位置环增益
        if (true)
        {
            row++; col = 0;
            label = new QLabel("12 [F32]位置环增益");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_posGain = new QLineEdit;
            m_lineEdit_configPage_posGain->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_posGain->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_posGain, row, col++);

            label = new QLabel();
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_posGain = new QPushButton; // 读取
            m_btn_configPage_read_posGain->setMaximumSize(30, 30);
            m_btn_configPage_read_posGain->setObjectName("read_posGain");
            m_btn_configPage_read_posGain->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_posGain->setToolTip("读取配置");
            connect(m_btn_configPage_read_posGain, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_posGain); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_posGain, row, col++);

            m_btn_configPage_write_posGain = new QPushButton; // 写入
            m_btn_configPage_write_posGain->setMaximumSize(30, 30);
            m_btn_configPage_write_posGain->setObjectName("write_posGain");
            m_btn_configPage_write_posGain->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_posGain->setToolTip("写入配置");
            connect(m_btn_configPage_write_posGain, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_posGain, row, col++);

            label = new QLabel("位置环增益，取值范围(0~1000)");
            gridBox->addWidget(label, row, col++);
        }

        // 13 [F32]转速环增益
        if (true)
        {
            row++; col = 0;
            label = new QLabel("13 [F32]转速环增益");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_velGain = new QLineEdit;
            m_lineEdit_configPage_velGain->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_velGain->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_velGain, row, col++);

            label = new QLabel();
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_velGain = new QPushButton; // 读取
            m_btn_configPage_read_velGain->setMaximumSize(30, 30);
            m_btn_configPage_read_velGain->setObjectName("read_velGain");
            m_btn_configPage_read_velGain->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_velGain->setToolTip("读取配置");
            connect(m_btn_configPage_read_velGain, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_velGain); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_velGain, row, col++);

            m_btn_configPage_write_velGain = new QPushButton; // 写入
            m_btn_configPage_write_velGain->setMaximumSize(30, 30);
            m_btn_configPage_write_velGain->setObjectName("write_velGain");
            m_btn_configPage_write_velGain->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_velGain->setToolTip("写入配置");
            connect(m_btn_configPage_write_velGain, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_velGain, row, col++);

            label = new QLabel("转速环增益，取值范围(0~1000)");
            gridBox->addWidget(label, row, col++);
        }

        // 14 [F32]转速环积分增益
        if (true)
        {
            row++; col = 0;
            label = new QLabel("14 [F32]转速环积分增益");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_velIntegratorGain = new QLineEdit;
            m_lineEdit_configPage_velIntegratorGain->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_velIntegratorGain->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_velIntegratorGain, row, col++);

            label = new QLabel();
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_velIntegratorGain = new QPushButton; // 读取
            m_btn_configPage_read_velIntegratorGain->setMaximumSize(30, 30);
            m_btn_configPage_read_velIntegratorGain->setObjectName("read_velIntegratorGain");
            m_btn_configPage_read_velIntegratorGain->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_velIntegratorGain->setToolTip("读取配置");
            connect(m_btn_configPage_read_velIntegratorGain, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_velIntegratorGain); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_velIntegratorGain, row, col++);

            m_btn_configPage_write_velIntegratorGain = new QPushButton; // 写入
            m_btn_configPage_write_velIntegratorGain->setMaximumSize(30, 30);
            m_btn_configPage_write_velIntegratorGain->setObjectName("write_velIntegratorGain");
            m_btn_configPage_write_velIntegratorGain->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_velIntegratorGain->setToolTip("写入配置");
            connect(m_btn_configPage_write_velIntegratorGain, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_velIntegratorGain, row, col++);

            label = new QLabel("转速环积分增益，取值范围(0~1000)");
            gridBox->addWidget(label, row, col++);
        }

        // 15 [F32]电流环控制带宽
        if (true)
        {
            row++; col = 0;
            label = new QLabel("15 [F32]电流环控制带宽");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_currentCtrlBW = new QLineEdit;
            m_lineEdit_configPage_currentCtrlBW->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_currentCtrlBW->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_currentCtrlBW, row, col++);

            label = new QLabel("[rad/s]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_currentCtrlBW = new QPushButton; // 读取
            m_btn_configPage_read_currentCtrlBW->setMaximumSize(30, 30);
            m_btn_configPage_read_currentCtrlBW->setObjectName("read_currentCtrlBW");
            m_btn_configPage_read_currentCtrlBW->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_currentCtrlBW->setToolTip("读取配置");
            connect(m_btn_configPage_read_currentCtrlBW, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_currentCtrlBW); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_currentCtrlBW, row, col++);

            m_btn_configPage_write_currentCtrlBW = new QPushButton; // 写入
            m_btn_configPage_write_currentCtrlBW->setMaximumSize(30, 30);
            m_btn_configPage_write_currentCtrlBW->setObjectName("write_velIntegratorGain");
            m_btn_configPage_write_currentCtrlBW->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_currentCtrlBW->setToolTip("写入配置");
            connect(m_btn_configPage_write_currentCtrlBW, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_currentCtrlBW, row, col++);

            label = new QLabel("电流环控制带宽，取值范围(200~60000)");
            gridBox->addWidget(label, row, col++);
        }

        // 16 [ENM]电机齿槽转矩补偿
        if (true)
        {
            row++; col = 0;
            label = new QLabel("16 [ENM]电机齿槽转矩补偿");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_cbox_configPage_anticoggingEnable = new QComboBox;
            m_cbox_configPage_anticoggingEnable->addItem("否", false);
            m_cbox_configPage_anticoggingEnable->addItem("是", true);
            m_cbox_configPage_anticoggingEnable->setMaximumWidth(130);
            gridBox->addWidget(m_cbox_configPage_anticoggingEnable, row, col++);

            label = new QLabel("");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_anticoggingEnable = new QPushButton; // 读取
            m_btn_configPage_read_anticoggingEnable->setMaximumSize(30, 30);
            m_btn_configPage_read_anticoggingEnable->setObjectName("read_anticoggingEnable");
            m_btn_configPage_read_anticoggingEnable->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_anticoggingEnable->setToolTip("读取配置");
            connect(m_btn_configPage_read_anticoggingEnable, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_anticoggingEnable); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_anticoggingEnable, row, col++);

            m_btn_configPage_write_anticoggingEnable = new QPushButton; // 写入
            m_btn_configPage_write_anticoggingEnable->setMaximumSize(30, 30);
            m_btn_configPage_write_anticoggingEnable->setObjectName("write_anticoggingEnable");
            m_btn_configPage_write_anticoggingEnable->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_anticoggingEnable->setToolTip("写入配置");
            connect(m_btn_configPage_write_anticoggingEnable, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_anticoggingEnable, row, col++);

            label = new QLabel("是否启用电机齿槽转矩补偿功能");
            gridBox->addWidget(label, row, col++);
        }

        // 17 [ENM]多轴同步功能
        if (true)
        {
            row++; col = 0;
            label = new QLabel("17 [ENM]多轴同步功能");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_cbox_configPage_syncTargetEnable = new QComboBox;
            m_cbox_configPage_syncTargetEnable->addItem("否", false);
            m_cbox_configPage_syncTargetEnable->addItem("是", true);
            m_cbox_configPage_syncTargetEnable->setMaximumWidth(130);
            gridBox->addWidget(m_cbox_configPage_syncTargetEnable, row, col++);

            label = new QLabel("");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_syncTargetEnable = new QPushButton; // 读取
            m_btn_configPage_read_syncTargetEnable->setMaximumSize(30, 30);
            m_btn_configPage_read_syncTargetEnable->setObjectName("read_syncTargetEnable");
            m_btn_configPage_read_syncTargetEnable->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_syncTargetEnable->setToolTip("读取配置");
            connect(m_btn_configPage_read_syncTargetEnable, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_syncTargetEnable); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_syncTargetEnable, row, col++);

            m_btn_configPage_write_syncTargetEnable = new QPushButton; // 写入
            m_btn_configPage_write_syncTargetEnable->setMaximumSize(30, 30);
            m_btn_configPage_write_syncTargetEnable->setObjectName("write_syncTargetEnable");
            m_btn_configPage_write_syncTargetEnable->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_syncTargetEnable->setToolTip("写入配置");
            connect(m_btn_configPage_write_syncTargetEnable, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_syncTargetEnable, row, col++);

            label = new QLabel("是否启用多轴同步功能，启用后驱动器收到运动指令后不会立即执行，而是等到同步指令后执行");
            gridBox->addWidget(label, row, col++);
        }

        // 18 [F32]转速到达窗口
        if (true)
        {
            row++; col = 0;
            label = new QLabel("18 [F32]转速到达窗口");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_targetVelWindow = new QLineEdit;
            m_lineEdit_configPage_targetVelWindow->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_targetVelWindow->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_targetVelWindow, row, col++);

            label = new QLabel("[turn/s]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_targetVelWindow = new QPushButton; // 读取
            m_btn_configPage_read_targetVelWindow->setMaximumSize(30, 30);
            m_btn_configPage_read_targetVelWindow->setObjectName("read_targetVelWindow");
            m_btn_configPage_read_targetVelWindow->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_targetVelWindow->setToolTip("读取配置");
            connect(m_btn_configPage_read_targetVelWindow, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_targetVelWindow); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_targetVelWindow, row, col++);

            m_btn_configPage_write_targetVelWindow = new QPushButton; // 写入
            m_btn_configPage_write_targetVelWindow->setMaximumSize(30, 30);
            m_btn_configPage_write_targetVelWindow->setObjectName("write_targetVelWindow");
            m_btn_configPage_write_targetVelWindow->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_targetVelWindow->setToolTip("写入配置");
            connect(m_btn_configPage_read_targetVelWindow, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_targetVelWindow, row, col++);

            label = new QLabel("当实际转速和目标转速差值的绝对值小于此值时认为位置到达目标转速，取值范围(0~100)");
            gridBox->addWidget(label, row, col++);
        }

        // 19 [F32]位置到达窗口
        if (true)
        {
            row++; col = 0;
            label = new QLabel("19 [F32]位置到达窗口");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_targetPosWindow = new QLineEdit;
            m_lineEdit_configPage_targetPosWindow->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_targetPosWindow->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_targetPosWindow, row, col++);

            label = new QLabel("[turn]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_targetPosWindow = new QPushButton; // 读取
            m_btn_configPage_read_targetPosWindow->setMaximumSize(30, 30);
            m_btn_configPage_read_targetPosWindow->setObjectName("read_targetPosWindow");
            m_btn_configPage_read_targetPosWindow->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_targetPosWindow->setToolTip("读取配置");
            connect(m_btn_configPage_read_targetPosWindow, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_targetPosWindow); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_targetPosWindow, row, col++);

            m_btn_configPage_write_targetPosWindow = new QPushButton; // 写入
            m_btn_configPage_write_targetPosWindow->setMaximumSize(30, 30);
            m_btn_configPage_write_targetPosWindow->setObjectName("write_targetPosWindow");
            m_btn_configPage_write_targetPosWindow->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_targetPosWindow->setToolTip("写入配置");
            connect(m_btn_configPage_write_targetPosWindow, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_targetPosWindow, row, col++);

            label = new QLabel("当实际位置和目标位置差值的绝对值小于此值时认为位置到达目标位置，取值范围(0~100)");
            gridBox->addWidget(label, row, col++);
        }

        // 20 [F32]转矩爬升速度
        if (true)
        {
            row++; col = 0;
            label = new QLabel("20 [F32]转矩爬升速度");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_torqueRampRate = new QLineEdit;
            m_lineEdit_configPage_torqueRampRate->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_torqueRampRate->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_torqueRampRate, row, col++);

            label = new QLabel("[Nm/s]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_torqueRampRate = new QPushButton; // 读取
            m_btn_configPage_read_torqueRampRate->setMaximumSize(30, 30);
            m_btn_configPage_read_torqueRampRate->setObjectName("read_torqueRampRate");
            m_btn_configPage_read_torqueRampRate->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_torqueRampRate->setToolTip("读取配置");
            connect(m_btn_configPage_read_torqueRampRate, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_torqueRampRate); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_torqueRampRate, row, col++);

            m_btn_configPage_write_torqueRampRate = new QPushButton; // 写入
            m_btn_configPage_write_torqueRampRate->setMaximumSize(30, 30);
            m_btn_configPage_write_torqueRampRate->setObjectName("write_torqueRampRate");
            m_btn_configPage_write_torqueRampRate->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_torqueRampRate->setToolTip("写入配置");
            connect(m_btn_configPage_write_torqueRampRate, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_torqueRampRate, row, col++);

            label = new QLabel("转矩爬升速度，只在控制模式为[转矩爬升]时有效，取值范围(0~100)");
            gridBox->addWidget(label, row, col++);
        }

        // 21 [F32]转速爬升速度
        if (true)
        {
            row++; col = 0;
            label = new QLabel("21 [F32]转速爬升速度");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_velRampRate = new QLineEdit;
            m_lineEdit_configPage_velRampRate->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_velRampRate->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_velRampRate, row, col++);

            label = new QLabel("[turn/s^2]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_velRampRate = new QPushButton; // 读取
            m_btn_configPage_read_velRampRate->setMaximumSize(30, 30);
            m_btn_configPage_read_velRampRate->setObjectName("read_velRampRate");
            m_btn_configPage_read_velRampRate->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_velRampRate->setToolTip("读取配置");
            connect(m_btn_configPage_read_velRampRate, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_velRampRate); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_velRampRate, row, col++);

            m_btn_configPage_write_velRampRate = new QPushButton; // 写入
            m_btn_configPage_write_velRampRate->setMaximumSize(30, 30);
            m_btn_configPage_write_velRampRate->setObjectName("write_velRampRate");
            m_btn_configPage_write_velRampRate->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_velRampRate->setToolTip("写入配置");
            connect(m_btn_configPage_write_velRampRate, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_velRampRate, row, col++);

            label = new QLabel("转速爬升速度，只在控制模式为[转速爬升]时有效，取值范围(0~100)");
            gridBox->addWidget(label, row, col++);
        }

        // 22 [F32]位置过滤带宽
        if (true)
        {
            row++; col = 0;
            label = new QLabel("22 [F32]位置过滤带宽");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_posFilterBW = new QLineEdit;
            m_lineEdit_configPage_posFilterBW->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_posFilterBW->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_posFilterBW, row, col++);

            label = new QLabel("[rad/s]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_posFilterBW = new QPushButton; // 读取
            m_btn_configPage_read_posFilterBW->setMaximumSize(30, 30);
            m_btn_configPage_read_posFilterBW->setObjectName("read_posFilterBW");
            m_btn_configPage_read_posFilterBW->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_posFilterBW->setToolTip("读取配置");
            connect(m_btn_configPage_read_posFilterBW, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_posFilterBW); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_posFilterBW, row, col++);

            m_btn_configPage_write_posFilterBW = new QPushButton; // 写入
            m_btn_configPage_write_posFilterBW->setMaximumSize(30, 30);
            m_btn_configPage_write_posFilterBW->setObjectName("write_posFilterBW");
            m_btn_configPage_write_posFilterBW->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_posFilterBW->setToolTip("写入配置");
            connect(m_btn_configPage_write_posFilterBW, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_posFilterBW, row, col++);

            label = new QLabel("位置输入带宽，只在控制模式为[位置过滤]时有效，取值范围(2~5000)");
            gridBox->addWidget(label, row, col++);
        }

        // 23 [F32]轮廓模式最大转速
        if (true)
        {
            row++; col = 0;
            label = new QLabel("23 [F32]轮廓模式最大转速");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_profileVel = new QLineEdit;
            m_lineEdit_configPage_profileVel->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_profileVel->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_profileVel, row, col++);

            label = new QLabel("[turn/s]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_profileVel = new QPushButton; // 读取
            m_btn_configPage_read_profileVel->setMaximumSize(30, 30);
            m_btn_configPage_read_profileVel->setObjectName("read_profileVel");
            m_btn_configPage_read_profileVel->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_profileVel->setToolTip("读取配置");
            connect(m_btn_configPage_read_profileVel, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_profileVel); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_profileVel, row, col++);

            m_btn_configPage_write_profileVel = new QPushButton; // 写入
            m_btn_configPage_write_profileVel->setMaximumSize(30, 30);
            m_btn_configPage_write_profileVel->setObjectName("write_profileVel");
            m_btn_configPage_write_profileVel->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_profileVel->setToolTip("写入配置");
            connect(m_btn_configPage_write_profileVel, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_profileVel, row, col++);

            label = new QLabel("轮廓模式最大转速，只在控制模式为[轮廓位置]时有效，取值范围(2~100)");
            gridBox->addWidget(label, row, col++);
        }

        // 24 [F32]轮廓模式最大加速度
        if (true)
        {
            row++; col = 0;
            label = new QLabel("24 [F32]轮廓模式最大加速度");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_profileAccel = new QLineEdit;
            m_lineEdit_configPage_profileAccel->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_profileAccel->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_profileAccel, row, col++);

            label = new QLabel("[turn/s^2]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_profileAccel = new QPushButton; // 读取
            m_btn_configPage_read_profileAccel->setMaximumSize(30, 30);
            m_btn_configPage_read_profileAccel->setObjectName("read_profileAccel");
            m_btn_configPage_read_profileAccel->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_profileAccel->setToolTip("读取配置");
            connect(m_btn_configPage_read_profileAccel, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_profileAccel); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_profileAccel, row, col++);

            m_btn_configPage_write_profileAccel = new QPushButton; // 写入
            m_btn_configPage_write_profileAccel->setMaximumSize(30, 30);
            m_btn_configPage_write_profileAccel->setObjectName("write_profileAccel");
            m_btn_configPage_write_profileAccel->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_profileAccel->setToolTip("写入配置");
            connect(m_btn_configPage_write_profileAccel, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_profileAccel, row, col++);

            label = new QLabel("轮廓模式最大加速度，只在控制模式为[轮廓位置]时有效，取值范围(0~1000)");
            gridBox->addWidget(label, row, col++);
        }

        // 25 [F32]轮廓模式最大减速度
        if (true)
        {
            row++; col = 0;
            label = new QLabel("25 [F32]轮廓模式最大减速度");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_profileDecel = new QLineEdit;
            m_lineEdit_configPage_profileDecel->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_profileDecel->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_profileDecel, row, col++);

            label = new QLabel("[turn/s^2]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_profileDecel = new QPushButton; // 读取
            m_btn_configPage_read_profileDecel->setMaximumSize(30, 30);
            m_btn_configPage_read_profileDecel->setObjectName("read_profileDecel");
            m_btn_configPage_read_profileDecel->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_profileDecel->setToolTip("读取配置");
            connect(m_btn_configPage_read_profileDecel, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_profileDecel); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_profileDecel, row, col++);

            m_btn_configPage_write_profileDecel = new QPushButton; // 写入
            m_btn_configPage_write_profileDecel->setMaximumSize(30, 30);
            m_btn_configPage_write_profileDecel->setObjectName("write_profileDecel");
            m_btn_configPage_write_profileDecel->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_profileDecel->setToolTip("写入配置");
            connect(m_btn_configPage_write_profileDecel, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_profileDecel, row, col++);

            label = new QLabel("轮廓模式最大减速度，只在控制模式为[轮廓位置]时有效，取值范围(0~1000)");
            gridBox->addWidget(label, row, col++);
        }

        ///////////////////////////// 安全保护 /////////////////////////////
        label = new QLabel("安全保护:");
        label->setMaximumHeight(30);
        gridBox->addWidget(label, ++row, 0, 1, -1);
        // 分割线
        line = new QFrame;
        line->setFrameShape(QFrame::HLine);
        line->setFrameShadow(QFrame::Plain);
        gridBox->addWidget(line, ++row, 0, 1, -1);

        // 26 [F32]欠压保护阈值
        if (true)
        {
            row++; col = 0;
            label = new QLabel("26 [F32]欠压保护阈值");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_protectUnderVolt = new QLineEdit;
            m_lineEdit_configPage_protectUnderVolt->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_protectUnderVolt->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_protectUnderVolt, row, col++);

            label = new QLabel("[V]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_protectUnderVolt = new QPushButton; // 读取
            m_btn_configPage_read_protectUnderVolt->setMaximumSize(30, 30);
            m_btn_configPage_read_protectUnderVolt->setObjectName("read_protectUnderVolt");
            m_btn_configPage_read_protectUnderVolt->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_protectUnderVolt->setToolTip("读取配置");
            connect(m_btn_configPage_read_protectUnderVolt, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_protectUnderVolt); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_protectUnderVolt, row, col++);

            m_btn_configPage_write_protectUnderVolt = new QPushButton; // 写入
            m_btn_configPage_write_protectUnderVolt->setMaximumSize(30, 30);
            m_btn_configPage_write_protectUnderVolt->setObjectName("write_protectUnderVolt");
            m_btn_configPage_write_protectUnderVolt->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_protectUnderVolt->setToolTip("写入配置");
            connect(m_btn_configPage_write_protectUnderVolt, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_protectUnderVolt, row, col++);

            label = new QLabel("当母线电压小于此值时停止电机并报欠压保护错误，取值范围(0~50)");
            gridBox->addWidget(label, row, col++);
        }

        // 27 [F32]过压保护阈值
        if (true)
        {
            row++; col = 0;
            label = new QLabel("27 [F32]过压保护阈值");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_protectOverVolt = new QLineEdit;
            m_lineEdit_configPage_protectOverVolt->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_protectOverVolt->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_protectOverVolt, row, col++);

            label = new QLabel("[V]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_protectOverVolt = new QPushButton; // 读取
            m_btn_configPage_read_protectOverVolt->setMaximumSize(30, 30);
            m_btn_configPage_read_protectOverVolt->setObjectName("read_protectOverVolt");
            m_btn_configPage_read_protectOverVolt->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_protectOverVolt->setToolTip("读取配置");
            connect(m_btn_configPage_read_protectOverVolt, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_protectOverVolt); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_protectOverVolt, row, col++);

            m_btn_configPage_write_protectOverVolt = new QPushButton; // 写入
            m_btn_configPage_write_protectOverVolt->setMaximumSize(30, 30);
            m_btn_configPage_write_protectOverVolt->setObjectName("write_protectOverVolt");
            m_btn_configPage_write_protectOverVolt->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_protectOverVolt->setToolTip("写入配置");
            connect(m_btn_configPage_write_protectOverVolt, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_protectOverVolt, row, col++);

            label = new QLabel("当母线电压大于此值时停止电机并报过压保护错误，取值范围(0~50)");
            gridBox->addWidget(label, row, col++);
        }

        // 28 [F32]过流保护阈值
        if (true)
        {
            row++; col = 0;
            label = new QLabel("28 [F32]过流保护阈值");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_protectOverCurr = new QLineEdit;
            m_lineEdit_configPage_protectOverCurr->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_protectOverCurr->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_protectOverCurr, row, col++);

            label = new QLabel("[A]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_protectOverCurr = new QPushButton; // 读取
            m_btn_configPage_read_protectOverCurr->setMaximumSize(30, 30);
            m_btn_configPage_read_protectOverCurr->setObjectName("read_protectOverCurr");
            m_btn_configPage_read_protectOverCurr->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_protectOverCurr->setToolTip("读取配置");
            connect(m_btn_configPage_read_protectOverCurr, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_protectOverCurr); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_protectOverCurr, row, col++);

            m_btn_configPage_write_protectOverCurr = new QPushButton; // 写入
            m_btn_configPage_write_protectOverCurr->setMaximumSize(30, 30);
            m_btn_configPage_write_protectOverCurr->setObjectName("write_protectOverCurr");
            m_btn_configPage_write_protectOverCurr->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_protectOverCurr->setToolTip("写入配置");
            connect(m_btn_configPage_write_protectOverCurr, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_protectOverCurr, row, col++);

            label = new QLabel("当电机电流大于此值时停止电机并报过流保护错误，取值范围(0~30)");
            gridBox->addWidget(label, row, col++);
        }

        // 29 [F32]最大母线电流
        if (true)
        {
            row++; col = 0;
            label = new QLabel("29 [F32]最大母线电流");
            label->setMaximumWidth(200);
            gridBox->addWidget(label, row, col++);

            m_lineEdit_configPage_protectIBusMax = new QLineEdit;
            m_lineEdit_configPage_protectIBusMax->setValidator(new QRegExpValidator(QRegExp("^(-?\\d+)(\\.\\d+)?$"))); // 限制只能输入浮点数
            m_lineEdit_configPage_protectIBusMax->setMaximumWidth(130);
            gridBox->addWidget(m_lineEdit_configPage_protectIBusMax, row, col++);

            label = new QLabel("[A]");
            label->setMaximumWidth(unitLabelWidth);
            gridBox->addWidget(label, row, col++);

            m_btn_configPage_read_protectIBusMax = new QPushButton; // 读取
            m_btn_configPage_read_protectIBusMax->setMaximumSize(30, 30);
            m_btn_configPage_read_protectIBusMax->setObjectName("read_protectIBusMax");
            m_btn_configPage_read_protectIBusMax->setIcon(QIcon(":/Icon/upload.png"));
            m_btn_configPage_read_protectIBusMax->setToolTip("读取配置");
            connect(m_btn_configPage_read_protectIBusMax, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            m_vector_allReadCfgBtn->append(m_btn_configPage_read_protectIBusMax); // 将此读取按钮添加到容器
            gridBox->addWidget(m_btn_configPage_read_protectIBusMax, row, col++);

            m_btn_configPage_write_protectIBusMax = new QPushButton; // 写入
            m_btn_configPage_write_protectIBusMax->setMaximumSize(30, 30);
            m_btn_configPage_write_protectIBusMax->setObjectName("write_protectIBusMax");
            m_btn_configPage_write_protectIBusMax->setIcon(QIcon(":/Icon/download.png"));
            m_btn_configPage_write_protectIBusMax->setToolTip("写入配置");
            connect(m_btn_configPage_write_protectIBusMax, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked);
            gridBox->addWidget(m_btn_configPage_write_protectIBusMax, row, col++);

            label = new QLabel("当母线电流大于此值时停止电机并报过流保护错误，取值范围(0~10)");
            gridBox->addWidget(label, row, col++);
        }

        // 给QScrollArea设置幕布，这一步必须位于最后
        scrollArea->setWidget(scrollAreaWidgetContents);
    }

    ///
    /// 下面按钮区域用widget，高度固定，所有按钮水平布局
    ///
    if (true)
    {
        //        widget = new QWidget();
        //        widget->setMinimumHeight(40);
        //        widget->setMaximumHeight(40);

        subHBox = new QHBoxLayout();
        subHBox->setMargin(0);
        //        widget->setLayout(subHBox);

        subVBox->addLayout(subHBox);

        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding)); // 首部弹簧

        m_btn_cfgPage_readAll = new QPushButton("读取所有");
        connect(m_btn_cfgPage_readAll, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageReadAllConfig_Clicked);
        subHBox->addWidget(m_btn_cfgPage_readAll);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Maximum));

        m_btn_cfgPage_writeAll = new QPushButton("写入所有");
        subHBox->addWidget(m_btn_cfgPage_writeAll);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Maximum));

        m_btn_cfgPage_resetDefault = new QPushButton("恢复默认");
        connect(m_btn_cfgPage_resetDefault, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageResetAllConfig_Clicked);
        subHBox->addWidget(m_btn_cfgPage_resetDefault);
        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Maximum));

        m_btn_cfgPage_saveConfig = new QPushButton("保存配置");
        connect(m_btn_cfgPage_saveConfig, &QPushButton::clicked, this, &MainWindow::slot_BtnConfigPageSaveConfig_Clicked);
        subHBox->addWidget(m_btn_cfgPage_saveConfig);

        subHBox->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding)); // 尾部弹簧
    }
}

void MainWindow::slot_ShowSystemStateInfo(QString info)
{
    m_label_systemState->setText(info);

    QTimer* timer = new QTimer(this);
    timer->setSingleShot(true);
    connect(timer, &QTimer::timeout, this, [&]{
        m_label_systemState->clear();
    });
    timer->start(3000);
}

void MainWindow::slot_ShowTxErrInfo(QString info)
{
    m_label_txErr->setText(info);
}

void MainWindow::slot_ShowRxErrInfo(QString info)
{
    m_label_rxErr->setText(info);
}

void MainWindow::slot_ShowSerialStateInfo(QString info)
{
    m_label_serialState->setText(info);
}

void MainWindow::slot_SetMotorEnableLed(bool en)
{
    if (en)
    {
        m_label_motorEnable->setPixmap(QPixmap(":/Icon/greenled.png"));
    }
    else
    {
        m_label_motorEnable->clear();
    }
}

void MainWindow::slot_SetTargetReachedLed(bool en)
{
    if (en)
    {
        m_label_targetReached->setPixmap(QPixmap(":/Icon/greenled.png"));
    }
    else
    {
        m_label_targetReached->clear();
    }
}

void MainWindow::slot_SetCurrentLimitActiveLed(bool en)
{
    if (en)
    {
        m_label_currentLimitActive->setPixmap(QPixmap(":/Icon/redled.png"));
    }
    else
    {
        m_label_currentLimitActive->clear();
    }
}


int MainWindow::SerialSend(QByteArray data)
{
    if (m_serialPort->isOpen())
    {
        m_mutex_serialPort->lock();
        m_serialPort->write(data);
        m_mutex_serialPort->unlock();
    }

    return 0;
}


void MainWindow::InitSerilaPort()
{
    // 扫描所有可用串口
    QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();
    QListIterator<QSerialPortInfo> serialListIter(serialList);
    m_cbox_serial->clear(); // 先清空cbox
    while (serialListIter.hasNext())
    {
        QSerialPortInfo port = serialListIter.next();
        int vid = port.vendorIdentifier();
        int pid = port.productIdentifier();
        qDebug() << __FILE__ << __FUNCTION__ << __LINE__ << port.description() << vid << pid;
        if (vid == 1155 && pid == 22336)
        {
            m_cbox_serial->addItem(port.portName());
        }
    }

    // 添加波特率
    QList<qint32> serialBaudrate = QSerialPortInfo::standardBaudRates();
    QListIterator<qint32> serialBaudrateIter(serialBaudrate);
    m_cbox_baudrate->clear(); // 先清空cbox
    while (serialBaudrateIter.hasNext())
    {
        m_cbox_baudrate->addItem(QString::number(serialBaudrateIter.next()));
    }
    m_cbox_baudrate->setCurrentText("115200");

    // 构造串口对象
    if (m_serialPort == nullptr)
    {
        m_serialPort = new QSerialPort();
        if (m_serialPort->isOpen())
        {
            m_serialPort->clear();
            m_serialPort->close();
        }
        connect(m_serialPort, &QSerialPort::readyRead, this, &MainWindow::slot_Serial_ReadyRead);

        // 创建互斥锁
        m_mutex_serialPort = new QMutex();
    }
}



QByteArray MainWindow::MakeSerialCmdPackage(eSerialCmd cmd, int len, float val, int cfgIdx)
{
    SerialFrame_t frame;
    memset(&frame, 0, sizeof(SerialFrame_t));
    frame.head = 0xAA;
    frame.id = cmd;
    frame.dlc = len;

    switch (cmd)
    {
    case CAN_CMD_SET_TORQUE:
        float_to_data(val, &frame.data[0]);
        break;

    case CAN_CMD_SET_VELOCITY:
        float_to_data(val, &frame.data[0]);
        break;

    case CAN_CMD_SET_POSITION:
        float_to_data(val, &frame.data[0]);
        break;

    case CAN_CMD_GET_VALUE_1:
        int32_to_data((int)val, &frame.data[0]);
        break;
    case CAN_CMD_GET_VALUE_2:
        int32_to_data((int)val, &frame.data[0]);
        break;

    case CAN_CMD_SET_CONFIG:
    {
        int32_to_data(cfgIdx, &frame.data[0]); // 参数的索引

        QString param = m_map_indexToConfig->value(cfgIdx);
        int dataType = m_map_configToDataType->value(param);

        // 数据
        if (dataType == CONFIG_DATA_TYPE_FLOAT)
        {
            float_to_data(val, &frame.data[4]);
        }
        else if (dataType == CONFIG_DATA_TYPE_INT32)
        {
            int data = (int)val;
            int32_to_data(data, &frame.data[4]);
        }
        else
        {}
    }
        break;

    case CAN_CMD_GET_CONFIG:
        int32_to_data(cfgIdx, &frame.data[0]); // 参数的索引
        break;

    default:
        break;
    }

    // 对8字节数据进行crc校验
    frame.crc = crc32((uint8_t*)&frame.data[0], sizeof(frame.data));

    QByteArray data((char*)&frame, sizeof(SerialFrame_t));
    return data;
}

float MainWindow::GetConfigInputCtrlValByConfigName(QString param)
{
    QVariant val;

    if (param == "invertDir")
    {
        val = m_cbox_configPage_invertDir->currentData().toFloat();
    }
    else if (param == "inertia")
    {
        val = m_lineEdit_configPage_inertia->text().toFloat();
    }
    else if (param == "torqueConstant")
    {
        val = m_lineEdit_configPage_torqueConstant->text().toFloat();
    }
    else if (param == "motorPP")
    {
        val = m_lineEdit_configPage_motorPP->text().toFloat();
    }
    else if (param == "motorR")
    {
        val = m_lineEdit_configPage_motorR->text().toFloat();
    }
    else if (param == "motorL")
    {
        val = m_lineEdit_configPage_motorL->text().toFloat();
    }
    else if (param == "motorCurrentLimit")
    {
        val = m_lineEdit_configPage_motorCurrentLimit->text().toFloat();
    }
    else if (param == "motorVelLimit")
    {
        val = m_lineEdit_configPage_motorVelLimit->text().toFloat();
    }
    else if (param == "calibCurrent")
    {
        val = m_lineEdit_configPage_calibCurrent->text().toFloat();
    }
    else if (param == "calibVoltage")
    {
        val = m_lineEdit_configPage_calibVoltage->text().toFloat();
    }
    else if (param == "ctrlMode")
    {
        val = m_cbox_configPage_ctrlMode->currentData().toFloat();;
    }
    else if (param == "posGain")
    {
        val = m_lineEdit_configPage_posGain->text().toFloat();
    }
    else if (param == "velGain")
    {
        val = m_lineEdit_configPage_velGain->text().toFloat();
    }
    else if (param == "velIntegratorGain")
    {
        val = m_lineEdit_configPage_velIntegratorGain->text().toFloat();
    }
    else if (param == "currentCtrlBW")
    {
        val = m_lineEdit_configPage_currentCtrlBW->text().toFloat();
    }
    else if (param == "anticoggingEnable")
    {
        val = m_cbox_configPage_anticoggingEnable->currentData().toFloat();;
    }
    else if (param == "syncTargetEnable")
    {
        val = m_cbox_configPage_syncTargetEnable->currentData().toFloat();;
    }
    else if (param == "targetVelWindow")
    {
        val = m_lineEdit_configPage_targetVelWindow->text().toFloat();
    }
    else if (param == "targetPosWindow")
    {
        val = m_lineEdit_configPage_targetPosWindow->text().toFloat();
    }
    else if (param == "torqueRampRate")
    {
        val = m_lineEdit_configPage_torqueRampRate->text().toFloat();
    }
    else if (param == "velRampRate")
    {
        val = m_lineEdit_configPage_velRampRate->text().toFloat();
    }
    else if (param == "posFilterBW")
    {
        val = m_lineEdit_configPage_posFilterBW->text().toFloat();
    }
    else if (param == "profileVel")
    {
        val = m_lineEdit_configPage_profileVel->text().toFloat();
    }
    else if (param == "profileAccel")
    {
        val = m_lineEdit_configPage_profileAccel->text().toFloat();
    }
    else if (param == "profileDecel")
    {
        val = m_lineEdit_configPage_profileDecel->text().toFloat();
    }
    else if (param == "protectUnderVolt")
    {
        val = m_lineEdit_configPage_protectUnderVolt->text().toFloat();
    }
    else if (param == "protectOverVolt")
    {
        val = m_lineEdit_configPage_protectOverVolt->text().toFloat();
    }
    else if (param == "protectOverCurr")
    {
        val = m_lineEdit_configPage_protectOverCurr->text().toFloat();
    }
    else if (param == "protectIBusMax")
    {
        val = m_lineEdit_configPage_protectIBusMax->text().toFloat();
    }
    else
    {}

    return *((float*)val.data());
}

bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
    MSG* msg = reinterpret_cast<MSG*>(message);
    if(msg->message == WM_DEVICECHANGE)
    {
        PDEV_BROADCAST_PORT lpdb = (PDEV_BROADCAST_PORT)msg->lParam;

        switch(msg->wParam)
        {
        case DBT_DEVICEARRIVAL:
            InitSerilaPort();
            qDebug() << "刷新端口资源" << "驱动器连接";
            qDebug() << "DBT_DEVTYP_PORT 设备插入";
            break;
        case DBT_DEVICEREMOVECOMPLETE:
            InitSerilaPort();
            qDebug() << "刷新端口资源" << "驱动器断开";
            qDebug() << "DBT_DEVTYP_PORT USB设备拔出";
            break;
        }
    }

    return false;
}


void MainWindow::slot_TimSerialCmdSend_Timeout()
{
    ///
    /// 上位机下发命令，下位机接收命令后解析并回传
    /// 1. 大部分命令由用户界面的按钮下发
    /// 2. 波形命令由“调试”界面上的“显示波形”按钮触发，并且只触发一次，后续由上位机循环下发并接收回传
    /// 3. 如果有示波器工作，说明用户触发了“调试”界面的某个“显示波形”按钮
    ///

    // CAN_CMD_GET_VALUE_1
    if (m_is_debugPageScope1_working)
    {
        SerialSend(MakeSerialCmdPackage(CAN_CMD_GET_VALUE_1, 1, m_debugPage_currentDataType1));
    }

    // CAN_CMD_GET_VALUE_2
    if (m_is_debugPageScope2_working)
    {
        SerialSend(MakeSerialCmdPackage(CAN_CMD_GET_VALUE_2, 1, m_debugPage_currentDataType2));
    }
}

void MainWindow::slot_Serial_ReadyRead()
{
    QByteArray data = m_serialPort->readAll();

    if (lwrb_is_ready(&m_serialLwrb))
    {
        lwrb_write(&m_serialLwrb, data.data(), data.length());
    }
}

void MainWindow::slot_TimQcpPlot_Timeout()
{
    ///
    /// 串口队列数据处理定时器负责处理数据包，各个示波器的数据被放入独立的队列
    /// 1. 判断示波器是否工作
    /// 2. 判断示波器数据队列是否有可用数据
    /// 3. 取出数据显示到界面
    ///

    // “调试”界面示波器1
    if (m_is_debugPageScope1_working)
    {
        static QTime timeStart = QTime::currentTime();
        double key = timeStart.msecsTo(QTime::currentTime())/1000.0;

        // 队列不为空
        if (!m_queue_debugPageScope1->isEmpty())
        {
            m_qcp_debugPage_scope1->graph(0)->addData(key, m_queue_debugPageScope1->dequeue());
            m_qcp_debugPage_scope1->graph(0)->rescaleAxes();

            // make key axis range scroll with the data (at a constant range size of 8):
            m_qcp_debugPage_scope1->xAxis->setRange(key, 8, Qt::AlignRight);
            m_qcp_debugPage_scope1->yAxis->rescale(true);
            m_qcp_debugPage_scope1->replot();
        }
    }

    // “调试”界面示波器2
    if (m_is_debugPageScope2_working)
    {
        static QTime timeStart = QTime::currentTime();
        double key = timeStart.msecsTo(QTime::currentTime())/1000.0;

        // 队列不为空
        if (!m_queue_debugPageScope2->isEmpty())
        {
            m_qcp_debugPage_scope2->graph(0)->addData(key, m_queue_debugPageScope2->dequeue());
            m_qcp_debugPage_scope2->graph(0)->rescaleAxes(true);

            // make key axis range make key axis range scroll with the data (at a constant range size of 8):
            m_qcp_debugPage_scope2->xAxis->setRange(key, 8, Qt::AlignRight);
            m_qcp_debugPage_scope2->yAxis->rescale(true);
            m_qcp_debugPage_scope2->replot();
        }
    }

    // “校准”界面示波器1，电机与编码器校准
    if (true)
    {
        // 队列中有OFFSET_LUT_NUM个数据点
        if (m_vector_calibPageScope1->length() >= OFFSET_LUT_NUM)
        {
            QVector<double> key;
            for (int i = 0; i < OFFSET_LUT_NUM; i++)
            {
                key.append(i);
            }
            QVector<double> val;
            for (int i = 0; i < OFFSET_LUT_NUM; i++)
            {
                val.append(m_vector_calibPageScope1->at(i));
            }

            m_qcp_calibPage_encoderCalib->clearGraphs();
            m_qcp_calibPage_encoderCalib->addGraph();
            m_qcp_calibPage_encoderCalib->graph(0)->addData(key, val);
            m_qcp_calibPage_encoderCalib->yAxis->rescale(true);
            m_qcp_calibPage_encoderCalib->replot();

            // 清空此次数据
            m_vector_calibPageScope1->clear();
        }
    }

    // “校准”界面示波器2，电机抗齿槽数据
    if (true)
    {
        // 队列中有OFFSET_LUT_NUM*2个数据点
        if (m_vector_calibPageScope2->length() >= COGGING_MAP_NUM * 2)
        {
            // 先删除所有旧图像
            m_qcp_calibPage_anticogCalib->clearGraphs();

            // CW
            QVector<double> key1;
            for (int i = 0; i < COGGING_MAP_NUM; i++)
            {
                key1.append(i);
            }
            QVector<double> val1;
            for (int i = 0; i < COGGING_MAP_NUM; i++)
            {
                val1.append(m_vector_calibPageScope2->at(i));
            }
            m_qcp_calibPage_anticogCalib->addGraph();
            m_qcp_calibPage_anticogCalib->graph(0)->addData(key1, val1);
            m_qcp_calibPage_anticogCalib->yAxis->rescale(true);
            m_qcp_calibPage_anticogCalib->replot();

            // CCW
            QVector<double> key2;
            for (int i = 0; i < COGGING_MAP_NUM; i++)
            {
                key2.append(i);
            }
            QVector<double> val2;
            for (int i = COGGING_MAP_NUM; i < COGGING_MAP_NUM * 2; i++)
            {
                val2.append(m_vector_calibPageScope2->at(i));
            }
            m_qcp_calibPage_anticogCalib->addGraph();
            m_qcp_calibPage_anticogCalib->graph(1)->addData(key2, val2);
            m_qcp_calibPage_anticogCalib->yAxis->rescale(true);
            m_qcp_calibPage_anticogCalib->replot();

            m_vector_calibPageScope2->clear();
        }
    }
}

void MainWindow::slot_BtnConnect_Clicked()
{
    if (!m_serialPort->isOpen())
    {
        m_serialPort->setPortName(m_cbox_serial->currentText());
        if (!m_serialPort->open(QIODevice::ReadWrite))
        {
            qDebug() << m_cbox_serial->currentText() + "打开失败";

            slot_ShowSystemStateInfo("串口连接失败");
            return;
        }

        // 设置串口属性
        m_serialPort->setBaudRate(m_cbox_serial->currentText().toInt());
        m_serialPort->setDataBits(QSerialPort::Data8);
        m_serialPort->setStopBits(QSerialPort::OneStop);
        m_serialPort->setParity(QSerialPort::NoParity);
        m_serialPort->setFlowControl(QSerialPort::NoFlowControl);

        // 禁用串口号和波特率下拉列表
        m_cbox_serial->setEnabled(false);
        m_cbox_baudrate->setEnabled(false);

        // 替换连接按钮的图标和文本
        m_btn_connect->setIcon(QIcon(":/Icon/disconnect.png"));
        m_btn_connect->setText("断开");
    }
    else
    {
        // 停止电机
        //        QMetaObject::invokeMethod(m_btn_debugPage_stopMotor, "clicked", Qt::QueuedConnection);
        slot_BtnDebugPageStopMotor_Clicked();
        slot_BtnDebugPageStopMotor_Clicked();

        // 停止示波器
        //        QMetaObject::invokeMethod(m_btn_debugPage_scope1Work, "clicked", Qt::QueuedConnection);
        //        QMetaObject::invokeMethod(m_btn_debugPage_scope2Work, "clicked", Qt::QueuedConnection);
        if (m_is_debugPageScope1_working)
            slot_BtnDebugPageScope1Work_Clicked();
        if (m_is_debugPageScope2_working)
            slot_BtnDebugPageScope2Work_Clicked();

        // 清除并关闭串口
        m_serialPort->clear();
        m_serialPort->close();

        // 使能串口号和波特率下拉列表
        m_cbox_serial->setEnabled(true);
        m_cbox_baudrate->setEnabled(true);

        // 替换连接按钮的图标和文本
        m_btn_connect->setIcon(QIcon(":/Icon/connect.png"));
        m_btn_connect->setText("连接");
    }
}

void MainWindow::slot_BtnDebug_Clicked()
{
    ui->stackedWidget->setCurrentWidget(m_widget_debug);
}

void MainWindow::slot_BtnCalib_Clicked()
{
    ui->stackedWidget->setCurrentWidget(m_widget_calib);
}

void MainWindow::slot_BtnConfig_Clicked()
{
    ui->stackedWidget->setCurrentWidget(m_widget_config);
}

void MainWindow::slot_BtnClearError_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    QByteArray cmd = MakeSerialCmdPackage(CAN_CMD_ERROR_RESET);
    SerialSend(cmd);
}

void MainWindow::slot_BtnDebugPageScope1Work_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    // 读取数据源1
    m_debugPage_currentDataType1 = m_cbox_debugPage_source1->currentData().value<eDataType>();
    // 读取采样点数1
    QVariant sample = m_cbox_debugPage_sampleCount1->currentData();

    // 设置示波器1工作标志位
    m_is_debugPageScope1_working = !m_is_debugPageScope1_working;
    if (m_is_debugPageScope1_working)
    {
        // 此时处于运行状态，需要把图标更换为stop
        m_btn_debugPage_scope1Work->setIcon(QIcon(":/Icon/stop.png"));
        // 禁用数据源、采样点数下拉列表
        m_cbox_debugPage_source1->setEnabled(false);
        m_cbox_debugPage_sampleCount1->setEnabled(false);

        qDebug() << "示波器1 开始 工作";
    }
    else
    {
        // 此时处于停止状态，需要把图标更换为run
        m_btn_debugPage_scope1Work->setIcon(QIcon(":/Icon/run.png"));
        // 启用数据源、采样点数下拉列表
        m_cbox_debugPage_source1->setEnabled(true);
        m_cbox_debugPage_sampleCount1->setEnabled(true);

        qDebug() << "示波器1 停止 工作";
    }
}

void MainWindow::slot_BtnDebugPageScope2Work_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    // 读取数据源2
    m_debugPage_currentDataType2 = m_cbox_debugPage_source2->currentData().value<eDataType>();
    // 读取采样点数2
    QVariant sample = m_cbox_debugPage_sampleCount2->currentData();

    // 设置示波器2工作标志位
    m_is_debugPageScope2_working = !m_is_debugPageScope2_working;
    if (m_is_debugPageScope2_working)
    {
        // 此时处于运行状态，需要把图标更换为stop
        m_btn_debugPage_scope2Work->setIcon(QIcon(":/Icon/stop.png"));
        // 禁用数据源、采样点数下拉列表
        m_cbox_debugPage_source2->setEnabled(false);
        m_cbox_debugPage_sampleCount2->setEnabled(false);

        qDebug() << "示波器2 开始 工作";
    }
    else
    {
        // 此时处于停止状态，需要把图标更换为run
        m_btn_debugPage_scope2Work->setIcon(QIcon(":/Icon/run.png"));
        // 启用数据源、采样点数下拉列表
        m_cbox_debugPage_source2->setEnabled(true);
        m_cbox_debugPage_sampleCount2->setEnabled(true);

        qDebug() << "示波器2 停止 工作";
    }
}

void MainWindow::slot_BtnDebugPageStartMotor_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    QByteArray cmd = MakeSerialCmdPackage(CAN_CMD_MOTOR_ENABLE);
    SerialSend(cmd);
}

void MainWindow::slot_BtnDebugPageStopMotor_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    qDebug() << "slot_BtnDebugPageStopMotor_Clicked";

    QByteArray cmd = MakeSerialCmdPackage(CAN_CMD_MOTOR_DISABLE);
    SerialSend(cmd);
}

void MainWindow::slot_BtnDebugPageResetHome_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    QByteArray cmd = MakeSerialCmdPackage(CAN_CMD_SET_HOME);
    SerialSend(cmd);
}

void MainWindow::slot_BtnDebugPageSetTorque_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    float torque = m_lineEdit_debugPage_torque->text().toFloat();

    QByteArray cmd = MakeSerialCmdPackage(CAN_CMD_SET_TORQUE, 4, torque);
    SerialSend(cmd);
}

void MainWindow::slot_BtnDebugPageSetVel_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    float vel = m_lineEdit_debugPage_vel->text().toFloat();

    QByteArray cmd = MakeSerialCmdPackage(CAN_CMD_SET_VELOCITY, 4, vel);
    SerialSend(cmd);
}

void MainWindow::slot_BtnDebugPageSetPos_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    float pos = m_lineEdit_debugPage_pos->text().toFloat();

    QByteArray cmd = MakeSerialCmdPackage(CAN_CMD_SET_POSITION, 4, pos);
    SerialSend(cmd);
}

void MainWindow::slot_BtnTrackGeneratePageSendPos(float val)
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        qDebug() << "slot_BtnTrackGeneratePageSendPos" << "未连接！";
        return;
    }

    QByteArray cmd = MakeSerialCmdPackage(CAN_CMD_SET_POSITION, 4, val);
    SerialSend(cmd);
}

void MainWindow::slot_QcpDebugPageScope_MouseMove(QMouseEvent *e)
{
    //    if (sender() == m_qcp_debugPage_scope1)
    //    {
    //        // 获得鼠标位置处对应的横坐标数据x
    //        double x = m_qcp_debugPage_scope1->xAxis->pixelToCoord(e->pos().x());

    //        m_qcpcursor_debugPageScope1->setGraph(m_qcp_debugPage_scope1->graph(0)); // 将游标和该曲线图层相连接
    //        m_qcpcursor_debugPageScope1->setGraphKey(x); // 将游标横坐标设置成刚获得的横坐标数据x
    //        m_qcpcursor_debugPageScope1->setInterpolating(true); // 游标的纵坐标可以通过曲线数据线性插值自动获得
    //        m_qcpcursor_debugPageScope1->updatePosition(); // 使得刚设置游标的横纵坐标位置生效
    //        // 更新游标说明的内容
    //        double yValue = m_qcpcursor_debugPageScope1->position->value();
    //        m_label_debugPage_cursor1->setText("值:" + QString::number(yValue));
    //        m_qcp_debugPage_scope1->replot(); // 重绘
    //    }
    //    else if (sender() == m_qcp_debugPage_scope2)
    //    {
    //        // 获得鼠标位置处对应的横坐标数据x
    //        double x = m_qcp_debugPage_scope2->xAxis->pixelToCoord(e->pos().x());

    //        m_qcpcursor_debugPageScope2->setGraph(m_qcp_debugPage_scope2->graph(0)); // 将游标和该曲线图层相连接
    //        m_qcpcursor_debugPageScope2->setGraphKey(x); // 将游标横坐标设置成刚获得的横坐标数据x
    //        m_qcpcursor_debugPageScope2->setInterpolating(true); // 游标的纵坐标可以通过曲线数据线性插值自动获得
    //        m_qcpcursor_debugPageScope2->updatePosition(); // 使得刚设置游标的横纵坐标位置生效
    //        // 更新游标说明的内容
    //        double yValue = m_qcpcursor_debugPageScope2->position->value();
    //        m_label_debugPage_cursor2->setText("值:" + QString::number(yValue));
    //        m_qcp_debugPage_scope2->replot(); // 重绘
    //    }
    //    else
    //    {}
}

void MainWindow::slot_QcpDebugPageScope_RightClicked(QMouseEvent *event)
{
    if (event->button() != Qt::RightButton)
        return;

    QMenu *pMenu = new QMenu();

    QAction *act_Clear = new QAction(tr("清空"), this);
    QAction *act_Reset = new QAction(tr("复位"), this);
    // 把QAction对象添加到菜单上
    pMenu->addAction(act_Clear);
    pMenu->addAction(act_Reset);
    // 连接鼠标右键点击信号

    //在鼠标右键点击的地方显示菜单
    pMenu->exec(cursor().pos());

    //释放内存
    QList<QAction*> list = pMenu->actions();
    foreach (QAction* pAction, list) delete pAction;
    delete pMenu;

    if (sender() == m_qcp_debugPage_scope1)
    {

    }
    else if (sender() == m_qcp_debugPage_scope2)
    {

    }
    else
    {}
}

void MainWindow::slot_ActionDebugPageScopeResetGraph_Triggered()
{

}

void MainWindow::slot_BtnCalibPageStartCalibMotorAndEncoder_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    QByteArray cmd = MakeSerialCmdPackage(CAN_CMD_CALIB_START);
    SerialSend(cmd);
}

void MainWindow::slot_BtnCalibPageStartCalibAnticogging_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    QByteArray cmd = MakeSerialCmdPackage(CAN_CMD_ANTICOGGING_START);
    SerialSend(cmd);
}

void MainWindow::slot_SetCalibPageMotorR(float r)
{
    m_label_calibPage_motorR->setNum(r);
}

void MainWindow::slot_SetCalibPageMotorL(float l)
{
    m_label_calibPage_motorL->setNum(l);
}

void MainWindow::slot_SetCalibPageMotorPP(int pp)
{
    m_label_calibPage_motorPP->setNum(pp);
}

void MainWindow::slot_SetCalibPageEncoderDir(int dir)
{
    m_label_calibPage_encoderDir->setNum(dir);
}

void MainWindow::slot_SetCalibPageEncoderOffset(int offset)
{
    m_label_calibPage_encoderOffset->setNum(offset);
}

void MainWindow::slot_BtnConfigPageSaveConfig_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    QByteArray cmd = MakeSerialCmdPackage(CAN_CMD_SAVE_ALL_CONFIG);
    SerialSend(cmd);
}

void MainWindow::slot_BtnConfigPageResetAllConfig_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    QByteArray cmd = MakeSerialCmdPackage(CAN_CMD_RESET_ALL_CONFIG);
    SerialSend(cmd);
}

void MainWindow::slot_BtnConfigPageReadWriteConfig_Clicked()
{
    // 先检测串口是否连接
    if (!m_serialPort->isOpen())
    {
        slot_ShowSystemStateInfo("未连接！");
        return;
    }

    QString btnObjName = sender()->objectName();
    QStringList subStr = btnObjName.split("_");
    QString action = subStr.at(0);
    QString param = subStr.at(1);

    QByteArray cmd;
    float val;
    int cfgIdx = m_map_configToIndex->value(param);
    if (action == "read")
    {
        cmd = MakeSerialCmdPackage(CAN_CMD_GET_CONFIG, 4, 0, cfgIdx);
    }
    else
    {
        val = GetConfigInputCtrlValByConfigName(param);
        cmd = MakeSerialCmdPackage(CAN_CMD_SET_CONFIG, 8, val, cfgIdx);
    }
    SerialSend(cmd);

    qDebug() << btnObjName << action << param << cfgIdx << val << __FILE__ << __FUNCTION__ << __LINE__;
}

void MainWindow::slot_BtnConfigPageReadAllConfig_Clicked()
{
    for (int i = 0; i < m_vector_allReadCfgBtn->length(); i++)
    {
        QPushButton* btn = m_vector_allReadCfgBtn->at(i);
        QMetaObject::invokeMethod(btn, "clicked", Qt::QueuedConnection);
    }
}

void MainWindow::slot_ReadConfig(QString param, int dataType, float val)
{
    ///
    /// 串口数据解析线程解析完 CAN_CMD_GET_CONFIG 命令后，
    /// 发送携带数据的信号给UI线程来显示数据
    ///

    if (param == "invertDir")
    {
        m_cbox_configPage_invertDir->setCurrentText((int)val == 0 ? "否" : "是");
    }
    else if (param == "inertia")
    {
        m_lineEdit_configPage_inertia->setText(QString::number(val));
    }
    else if (param == "torqueConstant")
    {
        m_lineEdit_configPage_torqueConstant->setText(QString::number(val));
    }
    else if (param == "motorPP")
    {
        m_lineEdit_configPage_motorPP->setText(QString::number(val));
    }
    else if (param == "motorR")
    {
        m_lineEdit_configPage_motorR->setText(QString::number(val));
    }
    else if (param == "motorL")
    {
        m_lineEdit_configPage_motorL->setText(QString::number(val));
    }
    else if (param == "motorCurrentLimit")
    {
        m_lineEdit_configPage_motorCurrentLimit->setText(QString::number(val));
    }
    else if (param == "motorVelLimit")
    {
        m_lineEdit_configPage_motorVelLimit->setText(QString::number(val));
    }
    else if (param == "calibCurrent")
    {
        m_lineEdit_configPage_calibCurrent->setText(QString::number(val));
    }
    else if (param == "calibVoltage")
    {
        m_lineEdit_configPage_calibVoltage->setText(QString::number(val));
    }
    else if (param == "ctrlMode")
    {
        m_cbox_configPage_ctrlMode->setCurrentIndex((int)val);
    }
    else if (param == "posGain")
    {
        m_lineEdit_configPage_posGain->setText(QString::number(val));
    }
    else if (param == "velGain")
    {
        m_lineEdit_configPage_velGain->setText(QString::number(val));
    }
    else if (param == "velIntegratorGain")
    {
        m_lineEdit_configPage_velIntegratorGain->setText(QString::number(val));
    }
    else if (param == "currentCtrlBW")
    {
        m_lineEdit_configPage_currentCtrlBW->setText(QString::number(val));
    }
    else if (param == "anticoggingEnable")
    {
        m_cbox_configPage_anticoggingEnable->setCurrentText((int)val == 0 ? "否" : "是");
    }
    else if (param == "syncTargetEnable")
    {
        m_cbox_configPage_syncTargetEnable->setCurrentText((int)val == 0 ? "否" : "是");
    }
    else if (param == "targetVelWindow")
    {
        m_lineEdit_configPage_targetVelWindow->setText(QString::number(val));
    }
    else if (param == "targetPosWindow")
    {
        m_lineEdit_configPage_targetPosWindow->setText(QString::number(val));
    }
    else if (param == "torqueRampRate")
    {
        m_lineEdit_configPage_torqueRampRate->setText(QString::number(val));
    }
    else if (param == "velRampRate")
    {
        m_lineEdit_configPage_velRampRate->setText(QString::number(val));
    }
    else if (param == "posFilterBW")
    {
        m_lineEdit_configPage_posFilterBW->setText(QString::number(val));
    }
    else if (param == "profileVel")
    {
        m_lineEdit_configPage_profileVel->setText(QString::number(val));
    }
    else if (param == "profileAccel")
    {
        m_lineEdit_configPage_profileAccel->setText(QString::number(val));
    }
    else if (param == "profileDecel")
    {
        m_lineEdit_configPage_profileDecel->setText(QString::number(val));
    }
    else if (param == "protectUnderVolt")
    {
        m_lineEdit_configPage_protectUnderVolt->setText(QString::number(val));
    }
    else if (param == "protectOverVolt")
    {
        m_lineEdit_configPage_protectOverVolt->setText(QString::number(val));
    }
    else if (param == "protectOverCurr")
    {
        m_lineEdit_configPage_protectOverCurr->setText(QString::number(val));
    }
    else if (param == "protectIBusMax")
    {
        m_lineEdit_configPage_protectIBusMax->setText(QString::number(val));
    }
    else
    {}
}


void CSerialDataConsumer::working()
{
    int ret = 0;

    SerialFrame_t frame;
    uint8_t packHead = 0xAA;
    size_t findIdx = 0;
    size_t getFull = 0;

    while (true)
    {
        // 先判断缓冲区有没有数据
        getFull = lwrb_get_full(m_pSerialLwrb);
        if (getFull >= sizeof(SerialFrame_t))
        {
            // 找到并取出最前面一包数据
            lwrb_find(m_pSerialLwrb, &packHead, 1, 0, &findIdx);
            lwrb_skip(m_pSerialLwrb, findIdx);
            lwrb_read(m_pSerialLwrb, (uint8_t*)&frame, sizeof(SerialFrame_t));

            // CRC校验
            if (frame.crc == crc32((uint8_t*)&frame.data[0], sizeof(frame.data)))
            {
                // 判断指令类型
                switch (GET_CMD(frame.id))
                {
                case CAN_CMD_MOTOR_DISABLE:
                    if (frame.dlc == 4)
                    {
                        ret = data_to_int32((uint8_t*)&frame.data[0]);
                        if (ret == -1)
                            emit signal_ShowSystemStatsInfo("Error code: " + QString::number(ret));
                        else if (ret == -2)
                            emit signal_ShowSystemStatsInfo("Calib invalid");
                        else if (ret == -3)
                            emit signal_ShowSystemStatsInfo("Invalid");
                        else
                            0;
                    }

                case CAN_CMD_MOTOR_ENABLE:
                    if (frame.dlc == 4)
                    {
                        ret = data_to_int32((uint8_t*)&frame.data[0]);
                        if (ret == -1)
                            emit signal_ShowSystemStatsInfo("Error code: " + QString::number(ret));
                        else if (ret == -2)
                            emit signal_ShowSystemStatsInfo("Calib invalid");
                        else if (ret == -3)
                            emit signal_ShowSystemStatsInfo("Invalid");
                        else
                            0;
                    }
                    break;

                case CAN_CMD_CALIB_START:
                    if (frame.dlc == 4)
                    {
                        ret = data_to_int32((uint8_t*)&frame.data[0]);
                        if (ret == 0)
                            emit signal_SetMotorEnableLed(true);
                        else if (ret == -1)
                            emit signal_ShowSystemStatsInfo("Error code: " + QString::number(ret));
                        else if (ret == -2)
                            emit signal_ShowSystemStatsInfo("Calib invalid");
                        else if (ret == -3)
                            emit signal_ShowSystemStatsInfo("Invalid");
                        else
                            0;
                    }
                    break;

                case CAN_CMD_CALIB_ABORT:
                    if (frame.dlc == 4)
                    {
                        ret = data_to_int32((uint8_t*)&frame.data[0]);
                        if (ret == 0)
                            emit signal_SetMotorEnableLed(true);
                        else if (ret == -1)
                            emit signal_ShowSystemStatsInfo("Error code: " + QString::number(ret));
                        else if (ret == -2)
                            emit signal_ShowSystemStatsInfo("Calib invalid");
                        else if (ret == -3)
                            emit signal_ShowSystemStatsInfo("Invalid");
                        else
                            0;
                    }
                    break;

                case CAN_CMD_ANTICOGGING_START:
                    if (frame.dlc == 4)
                    {
                        ret = data_to_int32((uint8_t*)&frame.data[0]);
                        if (ret == 0)
                            emit signal_SetMotorEnableLed(true);
                        else if (ret == -1)
                            emit signal_ShowSystemStatsInfo("Error code: " + QString::number(ret));
                        else if (ret == -2)
                            emit signal_ShowSystemStatsInfo("Calib invalid");
                        else if (ret == -3)
                            emit signal_ShowSystemStatsInfo("Invalid");
                        else
                            0;
                    }
                    break;

                case CAN_CMD_ANTICOGGING_ABORT:
                    if (frame.dlc == 4)
                    {
                        ret = data_to_int32((uint8_t*)&frame.data[0]);
                        if (ret == 0)
                            emit signal_SetMotorEnableLed(true);
                        else if (ret == -1)
                            emit signal_ShowSystemStatsInfo("Error code: " + QString::number(ret));
                        else if (ret == -2)
                            emit signal_ShowSystemStatsInfo("Calib invalid");
                        else if (ret == -3)
                            emit signal_ShowSystemStatsInfo("Invalid");
                        else
                            0;
                    }
                    break;

                case CAN_CMD_SET_HOME:
                    if (frame.dlc == 4)
                    {
                        ret = data_to_int32((uint8_t*)&frame.data[0]);
                        if (ret == 0)
                            emit signal_ShowSystemStatsInfo("Set Home Ok");
                        else
                            emit signal_ShowSystemStatsInfo("Set Home Error");
                    }
                    break;

                case CAN_CMD_ERROR_RESET:
                    qDebug() << "CAN_CMD_ERROR_RESET";
                    break;

                    //case CAN_CMD_GET_STATUSWORD:
                case CAN_CMD_STATUSWORD_REPORT:

                    ///
                    /// CAN_CMD_GET_STATUSWORD 由上位机主动下发
                    /// CAN_CMD_STATUSWORD_REPORT 由驱动器主动上传
                    ///

                    if (frame.dlc == 8)
                    {
                        uint32_t status = data_to_int32((uint8_t*)&frame.data[0]);
                        uint32_t errors = data_to_int32((uint8_t*)&frame.data[4]);

                        qDebug() << "CAN_CMD_STATUSWORD_REPORT" << status << errors;

                        // status
                        emit signal_SetMotorEnableLed((status & (0x1 << 0)));
                        emit signal_SetTargetReachedLed((status & (0x1 << 1)));
                        emit signal_SetCurrentLimitActiveLed((status & (0x1 << 2)));

                        // errors
                        if ((errors & (0x1 << 0)))
                            emit signal_ShowSystemStatsInfo("FATAL: ADC自检失败");
                        if ((errors & (0x1 << 1)))
                            emit signal_ShowSystemStatsInfo("FATAL: 编码器离线");

                        if ((errors & (0x1 << 16)))
                            emit signal_ShowSystemStatsInfo("ERROR: 过压");
                        if ((errors & (0x1 << 17)))
                            emit signal_ShowSystemStatsInfo("ERROR: 欠压");
                        if ((errors & (0x1 << 18)))
                            emit signal_ShowSystemStatsInfo("ERROR: 过流");
                    }
                    break;

                case CAN_CMD_GET_VALUE_1:
                    m_queue_debugPageScope1->append(data_to_float(&frame.data[0]));
                    break;
                case CAN_CMD_GET_VALUE_2:
                    m_queue_debugPageScope2->append(data_to_float(&frame.data[0]));
                    break;
                case CAN_CMD_CALIB_REPORT:
                    if (frame.dlc == 8)
                    {
                        int step = data_to_int32(&frame.data[0]);
                        qDebug() << "CAN_CMD_CALIB_REPORT" << step;
                        switch (step)
                        {
                        case 1:
                            signal_SetCalibPageMotorR(data_to_float(&frame.data[4]));
                            break;
                        case 2:
                            signal_SetCalibPageMotorL(data_to_float(&frame.data[4]));
                            break;
                        case 3:
                            signal_SetCalibPageMotorPP(data_to_int16(&frame.data[4]));
                            signal_SetCalibPageEncoderDir(data_to_int16(&frame.data[6]));
                            break;
                        case 4:
                            break;
                        case 5:
                            signal_SetCalibPageEncoderOffset(data_to_int32(&frame.data[4]));
                            break;

                        case 10:
                        default:
                            m_vector_calibPageScope1->append((float)data_to_int32(&frame.data[4]));
                            qDebug() << "编码器补偿数据点数" + QString::number(m_vector_calibPageScope1->length());
                            break;
                        }
                    }
                    break;
                case CAN_CMD_ANTICOGGING_REPORT:
                    m_vector_calibPageScope2->append((float)data_to_int16(&frame.data[4]));
                    qDebug() << "抗齿槽数据点数" + QString::number(m_vector_calibPageScope2->length());
                    break;

                case CAN_CMD_SET_CONFIG:
                    if (frame.dlc == 8)
                    {
                    }
                    break;

                case CAN_CMD_GET_CONFIG:

                    qDebug() << "CAN_CMD_GET_CONFIG" << frame.dlc;

                    if (frame.dlc == 8)
                    {
                        int cfgIdx = data_to_int32((uint8_t*)&frame.data[0]);
                        QString cfg = m_map_indexToConfig->value(cfgIdx);
                        int dataType = m_map_configToDataType->value(cfg);
                        float val = 0;

                        if (dataType == CONFIG_DATA_TYPE_FLOAT)
                        {
                            val = data_to_float((uint8_t*)&frame.data[4]);
                        }
                        else if (dataType == CONFIG_DATA_TYPE_INT32)
                        {
                            val = (float)data_to_int32((uint8_t*)&frame.data[4]);
                        }
                        else
                        {
                        }
                        emit signal_ReadConfig(cfg, dataType, val);

                        qDebug() << cfgIdx << val;
                    }
                    break;

                case CAN_CMD_SAVE_ALL_CONFIG:
                    if (frame.dlc == 4)
                    {
                        ret = data_to_int32((uint8_t*)&frame.data[0]);

                        if (ret == 0)
                            emit signal_ShowSystemStatsInfo(tr("保存配置成功"));
                        else
                            emit signal_ShowSystemStatsInfo(tr("保存配置失败"));

                        if (ret != 0)
                        {
                            signal_ShowWarningMessageBox(tr("警告"), tr("配置参数保存失败"));
                        }
                    }
                    break;

                case CAN_CMD_RESET_ALL_CONFIG:
                    if (frame.dlc == 0)
                    {
                        ret = data_to_int32((uint8_t*)&frame.data[0]);
                        if (ret == 0)
                            emit signal_ShowSystemStatsInfo(tr("重置配置成功"));
                        else
                            emit signal_ShowSystemStatsInfo(tr("重置配置失败"));
                    }
                    break;

                case CAN_CMD_GET_FW_VERSION:
                    if (frame.dlc == 8)
                    {
                        int major = data_to_int32((uint8_t*)&frame.data[0]);
                        int minor = data_to_int32((uint8_t*)&frame.data[4]);
                    }
                    break;

                default:
                    break;
                }
            }
            else
            {
                emit signal_ShowRxErrInfo("CRC Error");
            }
        }
    }
}
